Python爬虫实战 | 人民网爬虫 根据关键词筛选新闻文章

news/2024/12/21 20:47:34/

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

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

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

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

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

1. 爬取关键词搜索结果

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

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

1.1 分析网页

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

1.1.1 分析网页主要看什么

  • 我们能获取到哪些数据。
  • 服务器以什么形式发送数据
  • 如何获取全部数据(如何翻页)

1.1.2 如何使用浏览器的开发者工具

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

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

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

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

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

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

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

1.1.3 服务器返回的数据有哪些形式

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

HTML 格式

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

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

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

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

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").textdisplayTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(item["displayTime"]/1000))subtitle = item["subtitle"]title = BeautifulSoup(item["title"], "html.parser").texturl = 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 = 1end = 3kw = "春节"# 保存表头行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, kwreturn 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 = 1end = 3kw = "春节"# 保存表头行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 即可,替换方法同上。


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


http://www.ppmy.cn/news/973768.html

相关文章

python实战之网络爬虫(爬取网页新闻资讯列表)

关于大数据时代的数据挖掘 &#xff08;1&#xff09;为什么要进行数据挖掘&#xff1a;有价值的数据并不在本地存储&#xff0c;而是分布在广大的网路世界&#xff0c;我们需要将网络世界中的有价值数据挖掘出来供自己使用 &#xff08;2&#xff09;非结构化数据&#xff1…

Python爬虫——爬取某网站新闻

文章目录 前言一、基本目标二、使用步骤整体代码 结果总结 前言 &#x1f649;随机找了个网站爬爬&#xff0c;我们的目标是 1.利用爬虫的re、xpath等知识&#xff0c;爬取到这个官网上的新闻&#xff0c;内容有&#xff1a;新闻标题, 发布时间, 新闻链接, 阅读次数, 新闻来源…

Python - 通过requests实现腾讯新闻抓取爬虫

最近也是学习了一些爬虫方面的知识。以我自己的理解&#xff0c;通常我们用浏览器查看网页时&#xff0c;是通过浏览器向服务器发送请求&#xff0c;然后服务器响应以后返回一些代码数据&#xff0c;再经过浏览器解析后呈现出来。而爬虫则是通过程序向服务器发送请求&#xff0…

Python爬虫爬取新浪新闻内容

首先感谢丘祐玮老师在网易云课堂的Python网络爬虫实战课程,接下来也都是根据课程内容而写.一来算是自己的学习笔记&#xff0c;二来分享给大家参考之用。 课程视频大概是在16年11月录制的&#xff0c;现在是18年2月.其中有几处因网站更新升级产生的不同,小小修改后仍是爬虫学习…

SQL RIGHT JOIN 关键字

在 SQL 中&#xff0c;RIGHT JOIN 是一种用于将两个或多个表中的记录组合在一起的 JOIN 操作类型。RIGHT JOIN 操作将返回右侧表中所有的行&#xff0c;并通过连接条件从左侧表中匹配相应的行。如果左侧表中没有匹配的行&#xff0c;则会在结果集中填充 NULL 值。因此&#xff…

程序员面试系列,MySQL常见面试题?

原文链接 一、索引相关的面试题 &#xff08;1&#xff09;索引失效的情况有哪些 在MySQL查询时&#xff0c;以下情况可能会导致索引失效&#xff0c;无法使用索引进行高效的查询&#xff1a; 数据类型不匹配&#xff1a;如果查询条件中的数据类型与索引列的数据类型不匹配&…

微服务——Nacos配置管理

目录 Nacos配置管理——实现配置管理 配置管理实践 Nacos配置管理——微服务配置拉取 Nacos配置管理——配置热更新 方式一: ​编辑 方式二(推荐方式): Nacos配置管理——多环境配置共享 优先级问题 Nacos配置管理——nacos集群搭建 总结​编辑 Nacos配置管理——实现配置管…

EasyUI Datagrid 应用

两种为 datagrid 赋值表格 number 1 <div class"easyui-layout" data-options"fit:true"><div data-options"region:center"><table id"storeTable" class"easyui-datagrid" style"width:100%;height:…