logo资料库

Python-实现微信公众爬虫.pdf

第1页 / 共24页
第2页 / 共24页
第3页 / 共24页
第4页 / 共24页
第5页 / 共24页
第6页 / 共24页
第7页 / 共24页
第8页 / 共24页
资料共24页,剩余部分请下载后查看
微信公众号爬⾍的基本原理 ⽹上关于爬⾍的教程多如⽜⽑,但很少有看到微信公众号爬⾍教程,要有也是基于搜狗微信的,不过搜狗提供的数据有诸多弊端,⽐如⽂章链接是临时的,⽂章没有阅读量等指标,所以我想写⼀个⽐较 系统的关于如何通过⼿机客户端利⽤ Python 爬微信公众号⽂章的教程,并对公众号⽂章做数据分析,为更好的运营公众号提供决策。 爬⾍的基本原理 所谓爬⾍就是⼀个⾃动化数据采集⼯具,你只要告诉它要采集哪些数据,丢给它⼀个 URL,就能⾃动地抓取数据了。其背后的基本原理就是爬⾍程序向⽬标服务器发起 HTTP 请求,然后⽬标服务器返回 响应结果,爬⾍客户端收到响应并从中提取数据,再进⾏数据清洗、数据存储⼯作。 爬⾍的基本流程 爬⾍流程也是⼀个 HTTP 请求的过程,以浏览器访问⼀个⽹址为例,从⽤户输⼊ URL 开始,客户端通过 DNS 解析查询到⽬标服务器的 IP 地址,然后与之建⽴ TCP 连接,连接成功后,浏览器构造⼀个 HTTP 请求发送给服务器,服务器收到请求之后,从数据库查到相应的数据并封装成⼀个 HTTP 响应,然后将响应结果返回给浏览器,浏览器对响应内容进⾏数据解析、提取、渲染并最终展⽰在你⾯ 前。 HTTP 协议的请求和响应都必须遵循固定的格式,只有遵循统⼀的 HTTP 请求格式,服务器才能正确解析不同客户端发的请求,同样地,服务器遵循统⼀的响应格式,客户端才得以正确解析不同⽹站发 过来的响应。 HTTP 请求格式 HTTP 请求由请求⾏、请求头、空⾏、请求体组成。 请求⾏由三部分组成: 1. 第⼀部分是请求⽅法,常见的请求⽅法有 GET、POST、PUT、DELETE、HEAD 2. 第⼆部分是客户端要获取的资源路径 3. 第三部分是客户端使⽤的 HTTP 协议版本号 请求头是客户端向服务器发送请求的补充说明,⽐如 User-Agent 向服务器说明客户端的⾝份。 请求体是客户端向服务器提交的数据,⽐如⽤户登录时需要提⾼的账号密码信息。请求头与请求体之间⽤空⾏隔开。请求体并不是所有的请求都有的,⽐如⼀般的GET都不会带有请求体。 上图就是浏览器登录⾖瓣时向服务器发送的HTTP POST 请求,请求体中指定了⽤户名和密码。 HTTP 响应格式 HTTP 响应格式与请求的格式很相似,也是由响应⾏、响应头、空⾏、响应体组成。 响应⾏也包含三部分,分别是服务端的 HTTP 版本号、响应状态码、状态说明,响应状态码常见有 200、400、404、500、502、304 等等,⼀般以 2 开头的表⽰服务器正常响应了客户端请求,4 开头表⽰ 客户端的请求有问题,5 开头表⽰服务器出错了,没法正确处理客户端请求。状态码说明就是对该状态码的⼀个简短描述。 第⼆部分就是响应头,响应头与请求头对应,是服务器对该响应的⼀些附加说明,⽐如响应内容的格式是什么,响应内容的长度有多少、什么时间返回给客户端的、甚⾄还有⼀些 Cookie 信息也会放在响 应头⾥⾯。 第三部分是响应体,它才是真正的响应数据,这些数据其实就是⽹页的 HTML 源代码。 ⼩结 这仅仅只是⼀个爬⾍基本原理的介绍,涉及的 HTTP 协议的内容也⾮常有限,但不可能⽤⼀篇⽂章事⽆巨细的介绍完,因为 HTTP 协议是⼀个很⼤的话题,⽤⼀本书也写不完,深⼊了解推荐两本书《图 解HTTP》、《HTTP权威指南》。 使⽤ Requests 实现⼀个简单⽹页爬⾍ 友情提⽰:⼩册代码全部基于 Python3.6 实现 第⼀节我们简单介绍了爬⾍的基本原理,理解原理可以帮助我们更好的实现代码。Python 提供了⾮常多⼯具去实现 HTTP 请求,但第三⽅开源库提供的功能更丰富,你⽆需从 socket 通信开始写,⽐如使 ⽤Pyton内建模块 urllib 请求⼀个 URL 代码⽰例如下: import ssl from urllib.request import Request from urllib.request import urlopen context = ssl._create_unverified_context() # HTTP 请求 request = Request(url="https://foofish.net/pip.html", method="GET",
headers={"Host": "foofish.net"}, data=None) # HTTP 响应 response = urlopen(request, context=context) headers = response.info() # 响应头 content = response.read() # 响应体 code = response.getcode() # 状态码 发起请求前⾸先要构建请求对象 Request,指定 url 地址、请求⽅法、请求头,这⾥的请求体 data 为空,因为你不需要提交数据给服务器,所以你也可以不指定。urlopen 函数会⾃动与⽬标服务器建⽴连 接,发送 HTTP 请求,该函数的返回值是⼀个响应对象 Response,⾥⾯有响应头信息,响应体,状态码之类的属性。 但是,Python 提供的这个内建模块过于低级,需要写很多代码,使⽤简单爬⾍可以考虑 Requests,Requests 在GitHub 有近30k的Star,是⼀个很Pythonic的框架。先来简单熟悉⼀下这个框架的使⽤⽅式 安装 requests pip install requests GET 请求 >>> r = requests.get("https://httpbin.org/ip") >>> r # 响应对象 >>> r.status_code # 响应状态码 200 >>> r.content # 响应内容 '{\n "origin": "183.237.232.123"\n}\n' POST 请求 >>> r = requests.post('http://httpbin.org/post', data = {'key':'value'}) ⾃定义请求头 这个经常会⽤到,服务器反爬⾍机制会判断客户端请求头中的User-Agent是否来源于真实浏览器,所以,我们使⽤Requests经常会指定UA伪装成浏览器发起请求 >>> url = 'https://httpbin.org/headers' >>> headers = {'user-agent': 'Mozilla/5.0'} >>> r = requests.get(url, headers=headers) 参数传递 很多时候URL后⾯会有⼀串很长的参数,为了提⾼可读性,requests ⽀持将参数抽离出来作为⽅法的参数(params)传递过去,⽽⽆需附在 URL 后⾯,例如请求 url http://bin.org/get?key=val ,可使⽤ >>> url = "http://httpbin.org/get" >>> r = requests.get(url, params={"key":"val"}) >>> r.url u'http://httpbin.org/get?key=val' 指定Cookie Cookie 是web浏览器登录⽹站的凭证,虽然 Cookie 也是请求头的⼀部分,我们可以从中剥离出来,使⽤ Cookie 参数指定 >>> s = requests.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'}) >>> s.text u'{\n "cookies": {\n "from-my": "browser"\n }\n}\n' 设置超时 当发起⼀个请求遇到服务器响应⾮常缓慢⽽你又不希望等待太久时,可以指定 timeout 来设置请求超时时间,单位是秒,超过该时间还没有连接服务器成功时,请求将强⾏终⽌。 r = requests.get('https://google.com', timeout=5) 设置代理 ⼀段时间内发送的请求太多容易被服务器判定为爬⾍,所以很多时候我们使⽤代理IP来伪装客户端的真实IP。 import requests proxies = { 'http': 'http://127.0.0.1:1080', 'https': 'http://127.0.0.1:1080', } r = requests.get('http://www.kuaidaili.com/free/', proxies=proxies, timeout=2) Session 如果想和服务器⼀直保持登录(会话)状态,⽽不必每次都指定 cookies,那么可以使⽤ session,Session 提供的API和 requests 是⼀样的。 import requests s = requests.Session() s.cookies = requests.utils.cookiejar_from_dict({"a": "c"}) r = s.get('http://httpbin.org/cookies') print(r.text) # '{"cookies": {"a": "c"}}' r = s.get('http://httpbin.org/cookies') print(r.text) # '{"cookies": {"a": "c"}}' ⼩试⽜⼑ 现在我们使⽤Requests完成⼀个爬取知乎专栏⽤户关注列表的简单爬⾍为例,找到任意⼀个专栏,打开它的关注列表。⽤ Chrome 找到获取粉丝列表的请求地 址:https://www.zhihu.com/api/v4/columns/pythoneer/followers?include=data%5B%2A%5D.follower_count%2Cgender%2Cis_followed%2Cis_following&limit=10&offset=20。 我是怎么找到的?就是逐个点击左 侧的请求,观察右边是否有数据出现,那些以 .jpg,js,css 结尾的静态资源可直接忽略。
现在我们⽤ Requests 模拟浏览器发送请求给服务器,写程序前,我们要先分析出这个请求是怎么构成的,请求URL是什么?请求头有哪些?查询参数有哪些?只有清楚了这些,你才好动⼿写代码,掌握 分析⽅法很重要,否则⼀头雾⽔。 回到前⾯那个URL,我们发现这个URL是获取粉丝列表的接⼜,然后再来详细分析⼀下这个请求是怎么构成的。 请求URL:https://www.zhihu.com/api/v4/columns/pythoneer/followers 请求⽅法:GET user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 查询参数: include: data[*].follower_count,gender,is_followed,is_following offset: 0 limit: 10 利⽤这些请求数据我们就可以⽤requests这个库来构建⼀个请求,通过Python代码来抓取这些数据。 import requests class SimpleCrawler: def crawl(self, params=None): # 必须指定UA,否则知乎服务器会判定请求不合法
url = "https://www.zhihu.com/api/v4/columns/pythoneer/followers" # 查询参数 params = {"limit": 20, "offset": 0, "include": "data[*].follower_count, gender, is_followed, is_following"} headers = { "authority": "www.zhihu.com", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36", } response = requests.get(url, headers=headers, params=params) print("请求URL:", response.url) # 你可以先将返回的响应数据打印出来,拷⻉到 http://www.kjson.com/jsoneditor/ 分析其结构。 print("返回数据:", response.text) # 解析返回的数据 for follower in response.json().get("data"): print(follower) if __name__ == '__main__': SimpleCrawler().crawl() 输出: 请求URL: https://www.zhihu.com/api/v4/columns/pythoneer/followers?limit=20&offset=0&include=data%5B%2A%5D.follower_count%2C+gender%2C+is_followed%2C+is_following 返回数据: {'paging': {'is_end': False, 'totals': 10436, 'previous': 'http://www.zhihu.com/api/v4/columns/pythoneer/followers?include=data%5B%2A%5D.follower_count%2C+gender%2C+is_followed%2C+is_following&limit=20&offset=0' {'is_followed': False, 'avatar_url_template': 'https://pic4.zhimg.com/v2-71a02b83135bf403099b5d3f6a260c5f_{size}.jpg', 'user_type': 'people', 'is_following': False, 'headline': '欢迎关注公众号:「乔尔事务所」 → 城市 | 地理 | 规划 | 经济' {'is_followed': False, 'avatar_url_template': 'https://pic3.zhimg.com/v2-339c1f09cd793f43dbccae18af9bfbec_{size}.jpg', 'user_type': 'people', 'is_following': False, 'headline': '', 'url_token' {'is_followed': False, 'avatar_url_template': 'https://pic3.zhimg.com/v2-b6d02ae5a289dc93151c547fc3ae2837_{size}.jpg', 'user_type': 'people', 'is_following': False, 'headline': '2333333333' ... } Process finished with exit code 0 作业 上⾯的⽰例只抓到了20条粉丝数据,如何将该专栏的所有粉丝爬下来并保存到⽂件中。 ⼩结 这就是⼀个最简单的基于 Requests 的单线程知乎专栏粉丝列表的爬⾍,requests ⾮常灵活,请求头、请求参数、Cookie 信息都可以直接指定在请求⽅法中,返回值 response 如果是 json 格式可以直接调⽤ json()⽅法返回 python 对象。关于 Requests 的更多使⽤⽅法可以参考官⽅⽂档:http://docs.python-requests.org/en/master/ 使⽤ Fiddler 抓包分析公众号请求过程 上⼀节我们熟悉了 Requests 基本使⽤⽅法,配合 Chrome 浏览器实现了⼀个简单爬⾍,但因为微信公众号的封闭性,微信公众平台并没有对外提供 Web 端⼊⼜,只能通过⼿机客户端接收、查看公众号⽂ 章,所以,为了窥探到公众号背后的⽹络请求,我们需要借以代理⼯具的辅助。 HTTP代理⼯具又称为抓包⼯具,主流的抓包⼯具 Windows 平台有 Fiddler,macOS 有 Charles,阿⾥开源了⼀款⼯具叫 AnyProxy。它们的基本原理都是类似的,就是通过在⼿机客户端设置好代理IP和端 ⼜,客户端所有的 HTTP、HTTPS 请求就会经过代理⼯具,在代理⼯具中就可以清晰地看到每个请求的细节,然后可以分析出每个请求是如何构造的,弄清楚这些之后,我们就可以⽤ Python 模拟发起请 求,进⽽得到我们想要的数据。 Fiddler 下载地址是 https://www.telerik.com/download/fiddler,安装包就 4M 多,在配置之前,⾸先要确保你的⼿机和电脑在同⼀个局域⽹,如果不在同⼀个局域⽹,你可以买个随⾝WiFi,在你电脑上搭建 ⼀个极简⽆线路由器。安装过程⼀路点击下⼀步完成就可以了。 Fiddler 配置 选择 Tools > Fiddler Options > Connections Fiddler 默认的端⼜是使⽤ 8888,如果该端⼜已经被其它程序占⽤了,你需要⼿动更改,勾选 Allow remote computers to connect,其它的选择默认配置就好,配置更新后记得重启 Fiddler。⼀定要重启 Fiddler,否则代理⽆效。 接下来你需要配置⼿机,我们以 Android 设备为例,现在假设你的⼿机和电脑已经在同⼀个局域⽹(只要连的是同⼀个路由器就在同局域⽹内),找到电脑的 IP 地址,在 Fiddler 右上⾓有个 Online 图 标,⿏标移过去就能看到IP了,你也可以在CMD窗⼜使⽤ ipconfig 命令查看到 Android ⼿机代理配置 进⼊⼿机的 WLAN 设置,选择当前所在局域⽹的 WiFi 链接,设置代理服务器的 IP 和端⼜,我这是以⼩⽶设备为例,其它 Android ⼿机的配置过程⼤同⼩异。
测试代理有没有设置成功可以在⼿机浏览器访问你配置的地址:http://192.168.31.236:8888/ 会显⽰ Fiddler 的回显页⾯,说明配置成功。 现在你打开任意⼀个HTTP协议的⽹站都能看到请求会出现在 Fiddler 窗⼜,但是 HTTPS 的请求并没有出现在 Fiddler 中,其实还差⼀个步骤,需要在 Fiddler 中激活 HTTPS 抓取设置。在 Fiddler 选择 Tools > Fiddler Options > HTTPS > Decrypt HTTPS traffic, 重启 Fiddler。 为了能够让 Fiddler 截取 HTTPS 请求,客户端都需要安装且信任 Fiddler ⽣成的 CA 证书,否则会出现“⽹络出错,轻触屏幕重新加载:-1200”的错误。在浏览器打开 Fiddler 回显页⾯ http://192.168.31.236:8888/ 下载 FiddlerRoot certificate,下载并安装证书,并验证通过。 iOS下载安装完成之后还要从 设置->通⽤->关于本机->证书信任设置 中把 Fiddler 证书的开关打开 Android ⼿机下载保存证书后从系统设置⾥⾯找到系统安全,从SD卡安装证书,如果没有安装证书,打开微信公众号的时候会弹出警告。
⾄此,所有的配置都完成了,现在打开微信随便选择⼀个公众号,查看公众号的所有历史⽂章列表。微信在2018年6⽉份对 iOS 版本的微信以及部分 Android 版微信针对公众号进⾏了⼤幅调整,改为现在 的信息流⽅式,现在要获取某个公众号下⾯「所有⽂章列表」⼤概需要经过以下四个步骤: 如果你的微信版本还不是信息流⽅式展⽰的,那么应该是Android版本(微信采⽤的ABTest,不同的⽤户呈现的⽅式不⼀样) 同时观察 Fiddler 主⾯板 进⼊「全部消息」页⾯时,在 Fiddler 上已经能看到有请求进来了,说明公众号的⽂章⾛的都是HTTP协议,这些请求就是微信客户端向微信服务器发送的HTTP请求。 注意:第⼀次请求「全部消息」的时候你看到的可能是⼀⽚空⽩:
在Fiddler或Charles中看到的请求数据是这样的: 这个时候你要直接从左上⾓叉掉重新进⼊「全部消息」页⾯。 现在简单介绍⼀下这个请求⾯板上的每个模块的意义。 这样说明这个请求被微信服务器判定为⼀次⾮法的请求,这时你可以叉掉该页⾯重新进⼊「全部消息」页⾯。不出意外的话就能正常看到全部⽂章列表了,同时也能在Fiddler中看到正常的数据请求了。 我把上⾯的主⾯板划分为 7 ⼤块,你需要理解每块的内容,后⾯才有可能会⽤ Python 代码来模拟微信请求。 1、服务器的响应结果,200 表⽰服务器对该请求响应成功 2、请求协议,微信的请求协议都是基 于HTTPS 的,所以前⾯⼀定要配置好,不然你看不到 HTTPS 的请求。 3、微信服务器主机名 4、请求路径 5、请求⾏,包括了请求⽅法(GET),请求协议(HTTP/1.1),请求路径(/mp/profile_ext...后⾯还有很长⼀串参数) 6、包括Cookie信息在内的请求头。
7、微信服务器返回的响应数据,我们分别切换成 TextView 和 WebView 看⼀下返回的数据是什么样的。 TextView 模式下的预览效果是服务器返回的 HTML 源代码 WebView 模式是 HTML 代码经过渲染之后的效果,其实就是我们在⼿机微信中看到的效果,只不过因为缺乏样式,所以没有⼿机上看到的美化效果。 如果服务器返回的是 Json格式或者是 XML,你还可以切换到对应的页⾯预览查看。 ⼩结 配置好Fiddler的⼏个步骤主要包括指定监控的端⼜,开通HTTPS流量解密功能,同时,客户端需要安装CA证书。下⼀节我们基于Requests模拟像微信服务器发起请求。 抓取第⼀篇微信公众号⽂章 上⼀节我们熟悉了 Fiddler 的基本操作以及每个模块所代表的意义,这节我们详细了解获取微信公众号历史⽂章接⼜的请求情况,以及如何使⽤ Python 模拟微信发送请求获取公众号⽂章的基本信息。 打开微信历史消息页⾯,我们从 Fiddler 看到了很多请求,为了找到微信历史⽂章的接⼜,我们要逐个查看 Response 返回的内容,最后发现第 11 个请求 "https://mp.weixin.qq.com/mp/profile_ext? action=home..." 就是我们要寻找的(我是怎么找到的呢?这个和你的经验有关,你可以点击逐个请求,看看返回的Response内容是不是期望的内容) 确定微信公众号的请求HOST是 mp.weixin.qq.com 之后,我们可以使⽤过滤器来过滤掉不相关的请求。 爬⾍的基本原理就是模拟浏览器发送 HTTP 请求,然后从服务器得到响应结果,现在我们就⽤ Python 实现如何发送⼀个 HTTP 请求。这⾥我们使⽤ requests 库来发送请求。 创建⼀个 Pycharm 项⽬ 我们使⽤ Pycharm 作为开发⼯具,你也可以使⽤其它你熟悉的⼯具,Python 环境是 Python3(推荐使⽤ Python3.6),先创建⼀个项⽬ weixincrawler
分享到:
收藏