Python爬虫实战

您所在的位置:网站首页 中国新闻网英文网 Python爬虫实战

Python爬虫实战

2024-01-20 16:05| 来源: 网络整理| 查看: 265

上一篇《人民日报》的爬虫文章发布之后,收到了很不错的反馈,文中的爬虫代码也确实帮助到了很多人,我很开心。

跟读者们交流过程中,我也发现了一些比较共性的需求,就是 根据关键词筛选 新闻文章。

最初我的想法是,在爬取到全部文章数据的基础上,遍历文件夹,然后将正文中包含关键词的文章筛选出来。

如果你已经下载到了完整的新闻数据,那用这种方法无疑是最方便快捷的。但是如果没有的话,需要先爬取全部数据,再从中筛选符合条件的数据,无疑是有点浪费时间。

本篇文章,我将介绍两种方法,一种,是从现有数据中根据关键词筛选,另一种,是利用人民网的搜索功能,爬取关键词的搜索结果。

1. 爬取关键词搜索结果

最近有读者跟我请教问题,我才发现,人民网是有搜索功能的 (http://search.people.cn)。

所以只需要根据关键词检索,然后将搜索结果爬取下来即可。

1.1 分析网页

这里我简单教一下大家分析网页的一般思路。

1.1.1 分析网页主要看什么 我们能获取到哪些数据。服务器以什么形式发送数据如何获取全部数据(如何翻页) 1.1.2 如何使用浏览器的开发者工具

具体操作也很简单,按 F12 打开 开发者工具 ,切换到 Network ,然后刷新网页,可以看到列表中有很多请求。

其中有图片,js 代码, css 样式,html 源码等等等等各种各样的请求。

点击对应的请求项之后,你可以在 Preview 或者 Response 里预览该请求的数据内容,看是不是包含你需要的数据。

当然,你可以逐条去检查,也可以用顶部的过滤器去筛选请求类型(一般情况下,我们需要的数据,在 XHR 和 Doc 里即可找到。)

找到对应的请求之后,可以切换到 headers 查看该请求的请求头信息。

如图所示,主要关注 4 个地方。

Request URL:也就是请求的链接。爬虫请求的 url 填啥是要看这里,不要只知道去浏览器的地址栏里复制网址。Request Method:请求方法,有 GET 和 POST 两种,爬虫代码里用 requests.get() 还是 requests.post() 要与这里保持一致,否则可能无法正确获取数据。Request Headers:请求头,服务器会根据这个来判断是谁在访问网站,一般情况下,你需要设置爬虫请求头中的 User-Agent (有的网站可能需要判断 Accept ,Cookie ,Referer,Host 等,根据具体情况设置),来将爬虫伪装成正常的浏览器用户,防止被反爬机制拦截。Request Payload:请求参数,服务器会根据这些参数,来确定返回哪些数据给你,比如说,页码,关键词,等等,找到这些参数的规律,就可以通过构造这些参数,来直接向服务器获取数据了。 1.1.3 服务器返回的数据有哪些形式

一般情况下,有html 和 json 两种格式,接下来我简单教大家一下如何判断。

HTML 格式

一般情况下,是出现在筛选条件中的 Doc 类型里,也非常容易分辨,就是 Response 里面查看,全篇都是 这种标签的。

如果确定 html 源码中包含你需要的数据的话(这么说,是因为有些情况下数据是通过 js 代码动态加载进去的,直接解析源码是找不到数据的)

可以在 Elements 中,通过左上角的箭头按钮,来方便快捷地在网页中定位数据所在的标签(具体就不细讲了,自己试一下就懂了)。

大部分人入门学习爬虫,也都是从解析 html 开始的,应该比较熟悉。解析的方法也很多,如 正则表达式 ,BeautifulSoup , xpath 等等。

Json 格式

前面也说了,有些情况下,数据并不是放在 html 页面中直接返回的,而是通过其他的数据接口,动态的请求加载进去的。这样就导致的有些同学刚开始学爬虫的时候,明明在网页上分析的时候,标签路径都没问题,但是代码请求时死活找不到标签的情况。

这种动态加载数据的机制叫 Ajax ,感兴趣的可以去自行搜索了解。

Ajax 请求一般都是在请求类型中的 XHR 中,数据内容一般以 json 格式展示。(有些同学不知道怎么判断一个请求是不是 ajax,数据是不是 json,怎么办?这里说一个简单的判断方法,在 Preview 里看它是不是类似于下图的形式,大括号,键值对 {“xxx”: “xxx”},以及可以展开合上的小三角形)

这类请求返回的数据是 json 格式,可以直接使用 python 中的 json 库来解析,非常方便。

以上给大家简单讲解了一下,如何分析网页,如何抓包的方法,希望对大家有帮助。

言归正传,通过上面介绍的方法,我们可以很容易知道,人民网搜索结果的数据是通过 Ajax 形式发送的。

请求方法是 POST,请求链接,请求头,和请求参数都可以在 Headers 里查看到。

在参数里,我们可以看到 key 应该就是我们搜索的关键词,page 是页码,sortType 是搜索结果排序方式,等等等等,知道这些规律,这样我们就可以自行构造请求了。

1.2 试探反爬机制

一般网站为了防止攻击,都会或多或少设置一些反爬机制。这里给大家简单介绍一些常见的反爬机制,以及应对办法。

1.2.1 User-Agent

服务器会根据请求头的 User-Agent 字段来判断用户是通过什么来访问的,如:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.146 Safari/537.36

这里包含了浏览器,电脑系统的一些基本信息。如果你的 python 爬虫代码没有设置这个字段值的话,会默认是 python,这样服务器就可以大概判断出来这个请求是爬虫发起的,然后选择是否拦截。

应对办法也比较容易,就是用浏览器访问时候,把请求头里的 User-Agent 值复制下来,设置到代码里即可。

1.2.2 Referer

有些网站的资源是添加了防盗链的,也就是说,服务器处理请求的时候,是会去判断 Referer 的值的,只有在指定的站点发起请求,服务器才会允许返回数据(这样可以防止资源被盗用在其他网站使用)。

应对方法也简单,复制浏览器访问时,请求头里的 Referer 值即可。

1.2.3 Cookie

有的网站,可能有的数据需要登录账号才能访问,这里就用到了 Cookie 值。

不设置 Cookie , 设置未登录时候访问的 Cookie ,设置登录账号以后的 Cookie ,可能返回的数据结果都不一样。

应对方法因网站而异,如果不设置 Cookie 也可以访问,那就不用管;如果需要设置才能访问,那就根据情况(是否要登录,是否要会员等等)复制浏览器请求头中的 Cookie 值来设置。

1.2.4 JS 参数加密

请求的参数里,可能会有一些类似于乱码的参数,你不知道它是干什么的但是它又很重要,也不是时间戳,不填或者随便填都会请求失败。

这种情况就比较难搞了,这是 js 算法加密过的参数,要想自行构造,需要模拟整个参数加密的算法。

不过由于这个加密过程是前端完成的,所以加密算法的 js 代码是可以完全获取的到的,懂一些前端知识的,或者 Js 逆向的话,可以试着破解一下。

我个人是不建议这么做的,一是破解麻烦,二是可能会违法。

换个方法,用 selenium 或者 ``pyppeteer` 自动化去爬,它不香嘛。

1.2.5 爬取频率限制

如果长时间高频率地爬取数据的话,对网站服务器的压力是很大的,而且正常人访问也不可能做到这么高强度的访问(比如一秒访问十几次网站),一看就是爬虫干的。所以服务器一般会设置一个访问频率阈值,比如一分钟内如果发起超过 300 次请求,就视为爬虫,对其 IP 进行访问限制。

应对的话,我建议,如果不是特别赶时间的话,可以设置一个延迟函数,每爬取一次数据,随机睡眠几秒钟,使得访问频率降低到阈值以下,降低对服务器访问的压力,也减少封IP的几率。

1.2.6 其他

还有一些不太常见的,但是也比较有意思的反爬机制,给大家举几个例子。

字体反爬,就是网站对一些关键数据使用了特殊的字体,只有用对应的字体文件去解析,才能正确显示,否则源码里只能看到一串乱码。反调试,就是网站通过屏蔽浏览器开发者工具,屏蔽 selenium 等常用的爬虫工具,来达到一定的反爬效果。蜜罐反爬,就是检测到你是爬虫的时候,不会直接拒绝访问,而是给你返回一些假数据。爬虫者可能爬完了才发现是假数据,而且甚至不知道是什么时候开始被替换成假数据的。

以上是常见的一些反爬机制,希望对大家有帮助。

经过测试,人民网的反爬机制并不是特别严格,参数设置对了的话,基本上爬取不会受到什么限制。

不过如果是比较大量的数据爬取时,最好设置好爬取延时 以及 断点续爬 功能。

1.3 完善代码

首先导入需要的库。

每个库在本爬虫代码中的用处在注释中已经标注。

import requests # 发起网络请求 from bs4 import BeautifulSoup # 解析HTML文本 import pandas as pd # 处理数据 import os import time # 处理时间戳 import json # 用来解析json文本

发起网络请求函数 fetchUrl

函数用途及三个参数的含义,已在代码注释中标注,返回值为 json 类型的数据

''' 用于发起网络请求 url : Request Url kw : Keyword page: Page number ''' def fetchUrl(url, kw, page): # 请求头 headers={ "Accept": "application/json, text/plain, */*", "Content-Type": "application/json;charset=UTF-8", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36", } # 请求参数 payloads = { "endTime": 0, "hasContent": True, "hasTitle": True, "isFuzzy": True, "key": kw, "limit": 10, "page": page, "sortType": 2, "startTime": 0, "type": 0, } # 发起 post 请求 r = requests.post(url, headers=headers, data=json.dumps(payloads)) return r.json()

数据解析函数 parseJson

解析 json 对象,然后将解析到的数据包装成数组返回

def parseJson(jsonObj): #解析数据 records = jsonObj["data"]["records"]; for item in records: # 这里示例解析了几条,其他数据项如末尾所示,有需要自行解析 pid = item["id"] originalName = item["originalName"] belongsName = item["belongsName"] content = BeautifulSoup(item["content"], "html.parser").text displayTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(item["displayTime"]/1000)) subtitle = item["subtitle"] title = BeautifulSoup(item["title"], "html.parser").text url = item["url"] yield [[pid, title, subtitle, displayTime, originalName, belongsName, content, url]]

数据保存函数 saveFile

''' 用于将数据保存成 csv 格式的文件(以追加的模式) path : 保存的路径,若文件夹不存在,则自动创建 filename: 保存的文件名 data : 保存的数据内容 ''' def saveFile(path, filename, data): # 如果路径不存在,就创建路径 if not os.path.exists(path): os.makedirs(path) # 保存数据 dataframe = pd.DataFrame(data) dataframe.to_csv(path + filename + ".csv", encoding='utf_8_sig', mode='a', index=False, sep=',', header=False )

主函数

if __name__ == "__main__": # 起始页,终止页,关键词设置 start = 1 end = 3 kw = "春节" # 保存表头行 headline = [["文章id", "标题", "副标题", "发表时间", "来源", "版面", "摘要", "链接"]] saveFile("./data/", kw, headline) #爬取数据 for page in range(start, end + 1): url = "http://search.people.cn/api-search/elasticSearch/search" html = fetchUrl(url, kw, page) for data in parseJson(html): saveFile("./data/", kw, data) print("第{}页爬取完成".format(page)) # 爬虫完成提示信息 print("爬虫执行完毕!数据已保存至以下路径中,请查看!") print(os.getcwd(), "\\data")

以上为本爬虫的全部代码,大家可以在此基础上进行修改来使用,仅供学习交流,切勿用于违法用途。

注:这里没有写正文爬取的代码,一是人民网文章正文爬取的函数在上一篇文章中已经写了,大家有需要的话可以自行整合代码;二是,爬取正文的话会引入一些其他的问题,比如链接失效,文章来源于不同网站,解析方式不同等问题,说来话长,本文主要以思路讲解为主。

1.4 成果展示 1.4.1 程序运行效果

1.4.2 爬到的数据展示

image-20210206201915970

2. 利用现有数据筛选

如果你已经提前下载到了全部的新闻文章数据,那用这种方法无疑是最方便的,省去了漫长的爬取数据的过程,也省得跟反爬机制斗智斗勇。

2.1 数据来源

下载地址:https://github.com/caspiankexin/people-daily-crawler-date

以上是一个读者朋友爬取的人民日报新闻数据,包含从 19 年起至今的数据,每月一更新,应该可以满足很大一部分人对数据的需求了。

此外我还有之前爬的 18 年全年的数据,如果有需要的朋友,可以私聊找我要。

2.2 检索代码

以如下图所示的目录结构为例。

image-20210206202425501

假设我们有一些关键词,需要检测这些新闻文章中哪篇包含有关键词。

import os # 这里是你文件的根目录 path = "D:\\Newpaper\\2018" # 遍历path路径下的所有文件(包括子文件夹下的文件) def iterFilename(path): #将os.walk在元素中提取的值,分别放到root(根目录),dirs(目录名),files(文件名)中。 for root, dirs, files in os.walk(path): for file in files: # 根目录与文件名组合,形成绝对路径。 yield os.path.join(root,file) # 检查文件中是否包含关键词,若包含返回True, 若不包含返回False def checkKeyword(filename, kwList): with open(filename, "r", encoding="utf-8") as f: content = f.read() for kw in kwList: if kw in content: return True, kw return False, "" if __name__ == "__main__": # 关键词数组 kwList = ["经济", "贸易"] #遍历文章 for file in iterFilename(path): res, kw = checkKeyword(file, kwList) if res: # 如果包含关键词,打印文件名和匹配到的关键词 print("文件 ", file," 中包含关键词 ", kw) 2.3 运行结果

运行程序,即可从文件中筛选出包含关键词的文章。

2021年9月9日更新

最近有读者在复现文中的爬虫时,发现有如下报错:

in parseJson records = jsonObj[“data”][“records”]; TypeError:“data”

经过调试检查后,发现这个报错是因为原网站中,关键词搜索的接口更换了,导致数据获取失败。

解决办法:

将主函数部分的 url 替换成新的接口 http://search.people.cn/api-search/front/search 即可,如下所示。

if __name__ == "__main__": # 起始页,终止页,关键词设置 start = 1 end = 3 kw = "春节" # 保存表头行 headline = [["文章id", "标题", "副标题", "发表时间", "来源", "版面", "摘要", "链接"]] saveFile("./data/", kw, headline) #爬取数据 for page in range(start, end + 1): # url = "http://search.people.cn/api-search/elasticSearch/search" url = "http://search.people.cn/api-search/front/search" html = fetchUrl(url, kw, page) for data in parseJson(html): saveFile("./data/", kw, data) print("第{}页爬取完成".format(page)) # 爬虫完成提示信息 print("爬虫执行完毕!数据已保存至以下路径中,请查看!") print(os.getcwd(), "\\data")

2021年12月13日更新

最近有读者反馈,程序运行又报错了,可能是网站那个接口又换了。

我查看了一下,确实是这样。

只需要将接口替换为 http://search.people.cn/search-platform/front/search 即可,替换方法同上。

如果文章中有哪里没有讲明白,或者讲解有误的地方,欢迎在评论区批评指正,或者扫描下面的二维码,加我微信,大家一起学习交流,共同进步。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3