上一篇《Python 网络爬虫实战:爬取人民日报新闻文章》发布之后,确实帮到了不少朋友。
前几天,我好哥们问我:我想爬另一个日报新闻网站,网页结构几乎跟人民日报几乎一模一样,但是我用你的那个代码去爬却爬不下来数据呢?
顺着哥儿们发来的网址(网站传送地址:解放日报),我点进去看了一下,界面大概长这样。
跟人民日报的主页界面非常相似,都是 版面列表 -- 文章列表 -- 文章详情 的这种结构。
?本来我觉得肯定是我这哥儿们代码基础不过关,报的语法错误,先 “嘲讽” 他一波,然后帮他改好就得了。
没想到一分析,才发现这个网站的新闻数据,是 Ajax 动态加载出来的(具体区别就是,人民日报的数据是提前生成好在网页里,跟网页一起返回显示的;而解放日报则是分开的,先返回一个空网页,然后再通过数据的接口请求数据,把数据动态加载到空网页里显示)
虽然这个动态加载的爬虫也并不难,花了十来分钟就改完了,但是我感觉这还是蛮典型的一种类型的,所以趁这个机会拿来跟大家分享一下,遇到了这种类似的情况应该怎么做。
?
其实第一步应该是?明确需求?的,就是要明确我们需要什么样的数据,希望以什么样的形式才保存等等。不过由于我们这个跟《人民日报》爬虫目标一致,所以需求部分就暂且略过了。
我们直接来分析网站。
很多刚接触爬虫的同学,上来 F12 打开开发者工具,就咔咔定位数据找标签,如果是像人民日报那样的静态网页还好,你分析时看到的标签是什么样子,用代码爬的时候基本上也是那个样子;但是遇到解放日报这种动态加载的网站,就直接懵逼了,明明我标签位置,名字,class 和 id 什么的都没写错,为什么爬取的时候就总是报错说找不到标签呢?
答案就是,你找的那个标签是动态生成的,原始网页源码里根本没有,当然找不到了。
大家看下面,这是解放日报的版面导航列表。
一般大家爬取的时候,会先找到这个 <div class="dd-box"> 标签,然后在这个标签下找到所有的 <dd> 标签,然后再找 <a> 标签,然后就找到了想要的数据。
然鹅,当我们打开查看网页源码的时候(chrome 浏览器为例,鼠标右键,查看网页源代码),发现源代码里并没有我们需要的数据,而是一个类似于模板的东西。数据是通过后续动态的加载进来的。
当我们用爬虫去爬的时候,获取到的也是这样的源代码,当然取不到数据啦。
Tips1: 分析网页的时候,可以先查看一下网页源代码,看看自己需要的数据是否在里面,如果有,则可以继续接着分析,如果没有,说明数据是动态加载进来的,要换个思路。
既然网页源代码中找不到数据,那么我们去哪儿获得数据呢?
这就涉及到一个词,叫 “抓包” ,可能听上去很高深很难的样子,其实很简单的。我们知道数据肯定是通过发起 网络请求 获得的,就是网页向服务器发送一条请求,然后服务器把需要的数据回复回来,我们把网页向服务器发送的请求,和浏览器返回的数据,使用一些工具和手段截获下来进行分析,这个过程就是 “抓包”。
可能大家听着还是有点迷糊,下面我来具体演示一下。
打开开发者工具,切换到 Network,然后刷新网页(这里可以抓到网页加载过程中,向服务器发起的各种类型的请求)。
然后上图红框中圈出来的,就是我们抓取到的一条一条的请求包,有 js 脚本的,有 css 文件的,还有图片的等等各种类型的。我们要在这么多的 “请求包” 里找到包含我们需要的数据的包。
把列表里的这些请求从上到下一条一条的点开(在 Preview 里可以预览请求返回的数据),查看哪条请求是我们想要找的。
如上图箭头标识的请求点开以后,预览里的内容正好就是版面导航栏里的内容(预览里点击小箭头可以展开),我们成功找到了正确的请求。
也就是抓包成功!
包含数据的请求包我们是抓到了,但是我们具体要怎么用呢?怎么把它用到爬虫程序里,通过它来爬数据呢?
还是那条请求,我们切换到 Headers 页签,可以查看到关于这条请求的一些基本信息。
主要关注几个部分 Request URL(请求链接),Request Method(请求方法),Query String Parameters(请求参数),(当然请求头的那些东西,User-Agent ,Cookie 什么的,按照实际情况该怎么加就怎么加)。
我们的目的就是,通过 python 代码模拟浏览器发出这条请求,直接获取服务器返回的数据(返回的数据就是前面预览里的那些)。
import requests
url = "https://www.shobserver.com/staticsg/data/journal/2021-04-24/navi.json?ver=1619268138175"
r = requests.get(url)
print(r.text)
?我们简单写几行代码模拟一下这个过程(url 就是上图中 Request URL 的内容,requests.get() 是因为 Request Method 是 GET)。
运行结果如下,可以成功获得数据。
运行上面的代码,我们可以获得到 2021 年 4 月 24日的新闻数据,那我们如果想爬其他日期的新闻数据该怎么办呢?
这里我们观察一下请求的 url
https://www.shobserver.com/staticsg/data/journal/2021-04-24/navi.json?ver=1619268138175
其中有一段 2021-04-24 的字样,我们猜测,这个可能就是用来控制获取数据的日期的,改成别的日期 比如 2021-04-20 再试一下。
https://www.shobserver.com/staticsg/data/journal/2021-04-20/navi.json?ver=1619268138175
发现同样可以成功。
这样我们就知道,可以通过修改 url 里的日期字符串,来爬取指定日期的数据。
该请求返回的数据,是 json 格式的字符串,我们需要用 json 库来进行解析。
(有同学可能想问了,那么一大串乱码似的文字,你怎么知道它是 json 格式的呢?简单来讲,看两个特点,一个是大括号 {} 包起来的,另一个是键值对格式,就是 xxx : xxx 这种形式的。实在不知道怎么判断的话,就去前面讲抓包的部分,看预览的地方,如果有小箭头能够折叠展开的,就是 json 格式)
我们可以看到 pages 里有版面的列表,每个版面的 articleList 里有文章列表,包含了我们需要的版面和文章列表信息。具体解析的 Python 代码这里就不讲了,文末会贴源码。
首先点开一个文章的正文页,用前面同样的分析方法过一遍,很容易知道,正文内容也是动态加载进来的,而且正文的数据是通过下面这条请求来获得到的。
我们简单写段代码来验证一下
import requests
url = "https://www.shobserver.com/staticsg/data/journal/2021-04-24/01/article/312840.json?ver=1619271661571"
r = requests.get(url)
print(r.text)
运行结果
经过对这条请求的 url 的分析,我们可以知道,/2021-04-24 是日期,/01 是指版面的编号,/312840 是文章的id。
https://www.shobserver.com/staticsg/data/journal/2021-04-24/01/article/312840.json?ver=1619271661571
?
至此,我们完成了对网站的分析,讲解了如何判断网站数据是动态加载还是静态加载,如果是动态加载的话如何抓包,抓包以后如何使用等等,并抓到了 新闻版面列表,文章列表,文章正文内容的请求接口。如果有哪里没有讲清楚,或者对以上内容有不太明白的地方,可以留言问我。
下面进行写代码,正式爬取。
?
下面是爬虫源码,供大家学习交流使用,请勿用于非法用途。
import requests
import bs4
import os
import datetime
import time
import json
def fetchUrl(url):
'''
功能:访问 url 的网页,获取网页内容并返回
参数:目标网页的 url
返回:目标网页的 html 内容
'''
headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
}
r = requests.get(url, headers=headers)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
def saveFile(content, path, filename):
'''
功能:将文章内容 content 保存到本地文件中
参数:要保存的内容,路径,文件名
'''
# 如果没有该文件夹,则自动生成
if not os.path.exists(path):
os.makedirs(path)
# 保存文件
with open(path + filename, 'w', encoding='utf-8') as f:
f.write(content)
def download_jfrb(year, month, day, destdir):
'''
功能:网站 某年 某月 某日 的新闻内容,并保存在 指定目录下
参数:年,月,日,文件保存的根目录
'''
url = 'https://www.shobserver.com/staticsg/data/journal/' + year + '-' + month + '-' + day + '/navi.json'
html = fetchUrl(url)
jsonObj = json.loads(html)
for page in jsonObj["pages"]:
pageName = page["pname"]
pageNo = page["pnumber"]
print(pageNo, pageName)
for article in page["articleList"]:
title = article["title"]
subtitle = article["subtitle"]
pid = article["id"]
url = "https://www.shobserver.com/staticsg/data/journal/" + year + '-' + month + '-' + day + "/" + str(pageNo) + "/article/" + str(pid) + ".json"
print(pid, title, subtitle)
html = fetchUrl(url)
cont = json.loads(html)["article"]["content"]
bsobj = bs4.BeautifulSoup(cont, 'html.parser')
content = title + subtitle + bsobj.text
print(content)
path = destdir + '/' + year + month + day + '/' + str(pageNo) + " " + pageName + "/"
fileName = year + month + day + '-' + pageNo + '-' + str(pid) + "-" + title + '.txt'
saveFile(content, path, fileName)
if __name__ == '__main__':
'''
主函数:程序入口
'''
# 爬取指定日期的新闻
newsDate = input('请输入要爬取的日期(格式如 20210416 ):')
year = newsDate[0:4]
month = newsDate[4:6]
day = newsDate[6:8]
download_jfrb(year, month, day, 'Data')
print("爬取完成:" + year + month + day)
以上是爬取单天的新闻文章的爬虫,如果希望爬取一段时间内的新闻文章数据,可以参照《Python 网络爬虫实战:爬取人民日报新闻文章》中的代码进行修改。
?
运行程序,输入 20210424 以后,爬虫自动爬取了 2021年4月24日的新闻数据,并保存在 Data / 20210424 /?目录下。
?
?
如果文章中有哪里没有讲明白,或者讲解有误的地方,欢迎在评论区批评指正,或者扫描下面的二维码,加我微信,大家一起学习交流,共同进步。
这年头,你看到的东西未必就是你认为的东西。一个mysql协议的后面,可能是tidb;...
Vue router 如何传参 params、query 是什么? params:/router1/:id,这里的 id ...
我是歌谣 放弃很容易 但是坚持一定很酷 前言 今天要做一个很常见的一个功能 就是...
账号安全和登录控制 一、账号安全控制 1.1 账号安全的基本措施 1.1.1 将非登录用...
文章目录 前言 JSON是什么 jsonpath Python中的jsonpath jsonpath语法 使用 结语...
近期频频遇到层次查询SQL的性能问题,结合历史故障案例,汇总了一些场景connect ...
2月27日消息 微软早些时候已恢复 因假期带来的更新延期,最新一次的 Windows 10 ...
cremeb多商户打包代码的时候总是重复的工作就想着写了个脚本记录一下sh脚本用到...
本文实例为大家分享了HTML简单购物数量小程序,供大家参考,具体内容如下 XML/HT...
据外媒报道,Windows上的邮件(Mail)应用一开始是一个非常令人兴奋的项目,但它慢...