如何通过爬虫爬取公众号的活跃度
- 2019 年 10 月 4 日
- 筆記
背景介绍
这篇文章主要来介绍下如何通过爬虫技术来爬取测试相关公众号的信息,接着通过对爬取的信息进行过滤处理给出测试公众号活跃度的一个列表。这里活跃度会以月发文的数量来进行衡量。
这篇文章的代码是在Python 3.x版本调试通过的。
爬取详解
现在来具体介绍下爬虫的步骤。因为搜狗搜索是可以进行微信公众号信息搜索的,因此我们主要是通过对搜索的搜索结果进行爬取,得到我们需要的信息,可通过如下的链接进行微信公众号信息搜索:
https://weixin.sogou.com/
输入需要搜索的公众号,然后点击右侧的搜公众号按钮即可,如下图所示:
爬虫的一般步骤是获取需要访问的URL,通过分析试验去构造请求的相关参数信息,接着发送请求,最后获取响应的信息进行处理。
爬取的URL我们可以借助浏览器的工具去获取,比如使用Chrome,可以鼠标右键选择Inspect进行查看即可。首先我们打开上面给链接,打开微信公众号搜索的首页,然后鼠标右键选择Inspect,接着点击Network tab, 点完后在搜索输入框随便输入一个搜索关键字,如软件测试,最后点击下搜公众号按钮,浏览器会自动化将这个动作所发生的请求都列出来,我们可以通过查看这些请求去获取我们需要爬取的URL和相关头部信息,如下所示:
通过观察分析上面所发生的请求以及进行一定的试验,可以知道获取公众号信息的关键请求是类似如下的URL:
https://weixin.sogou.com/weixin?type=1&query=%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95&ie=utf8&s_from=input&_sug_=n&_sug_type_=
其中的query字段的值根据经验可以容易知道是通过对搜索关键字"软件测试"进行urlencode过的的结果,因此到时候发请求的时候我们要介绍Python的库对搜索关键字先进行urlencode操作,其他几个字段值和上面的URL保持一致即可。
通过实验得知模拟发送上面的请求还需要添加如下的头部信息才能发送成功:
- host头部
- User-Agent头部
我们在来看下搜索结果的关于月发文数量的截图:
通过实验得知,通过一般的xpath并不能获取到月发文数量这个控件元素,因此需要再分析是否有其他途径可以获取到,经过分析发现,如下的URL返回的结果就包含的月发文的数量:
这个请求的URL拷贝出来,如下所示:
https://weixin.sogou.com/websearch/weixin/pc/anti_account.jsp?t=1567955232611&signature=afj0JGyeIpkQtGdWwBwy696pWaIImPGhhMt*eOEuVLeOXfRxcJE2DPUU51rZgzjIEvdlybryyq6Cujwsqru*MZPJghMn4B1Y1PKoBCjpnaAw-NDQjdMyEou5kvpozFDk7em6foePHdsJwW0NaBAJ43PB4Zv3ptqBFvwkXyWkmf*a2oyelgyNE6l7GsC8vcTD-5*4DXWvG-LaqAsAheIBIpuNDKGNzvTG5KKCe-hAFNT4R1I8e6MwEuMQGY8R32p8N8OzhAifZ7Fpfi92RwVlhZ4beoX0SUXAzqAPYaqBNxaY5hoiem*c-zkTlSr94XYBkczD-jg5eH75wvIHKpNejt*dgKW*G3g5yp4U2UYbadw1y1K*IGNLl15gsGydHjZtWpST1wQxY6XwVl8pe9DBaDwS30tTSsF3bxWq1WHPtRI=
可以发现这个URL很长,但仔细分析观察可以知道这个URL的前置是不变的也就是https://weixin.sogou.com,后面的部分其实第一个搜索的URL的响应结果里已经有包含的,如下所示:
因此我们只要对第一个请求URL的响应结果编写对应的正则提取出后面的URL然后和规定的前缀host进行拼接就可以构造出获取发文数量的URL了。发文URL的返回里仔细分析可以知道可以通过data-id关联到微信公众号名称,data-id就是如下截图圈出来的部分
我们就是通过上面红色圈出来的data-id去第一个搜索URL返回的结果里找到对应这个data-id的微信公众号名称,这样我们就可以完整获取到公众号和其对应的发文数量了。
我在爬虫程序的开始定义了个列表用来存放需要搜索的关键字,程序会遍历这个关键字列表,每次获取出该关键字搜索出来的公众号及其对应的月发文数量,在外部还定义了个字典用来存放公众号和对应月发文数量,最后遍历结束后会对这个字典按值进行降序的排列,这样就能得出搜索公众号的活跃度排名了。
完整代码
import requests import urllib.parse import re from lxml import etree #定义字典,要来存放公众号和对应的月发文数量 all_accounts = {} #定义需要搜索的公众号的关键字,可以修改这个列表进行你需要的公众号搜索 names = ['软件测试','性能测试','测试开发','自动化测试','接口测试','Appium','webdriver','selenium','51testing软件测试网','testerhome'] #遍历关键字列表,将每次检索出来的数据添加到上面的字典中,遍历结束就可以获取所有的搜索数据 for name in names: #对关键字进行urlencode query_str = urllib.parse.quote(name) url = 'https://weixin.sogou.com/weixin?type=1&query=' + query_str+'&ie=utf8&s_from=input&_sug_=y&_sug_type_=' headers = {} #设置必须的头部信息 headers['host'] = "weixin.sogou.com" headers['User-Agent'] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15" response = requests.get(url, headers=headers) #设置正则,用来提取获取月发文数量的URL后面的path post_count_regex = re.compile( '<script>var account_anti_url = "(.*?)";</script>') post_count_url = post_count_regex.findall(response.text)[0] data = requests.get('http://weixin.sogou.com{}'.format(post_count_url)) # 获取月发文数量,需要通过data-id关联获取 post_counts = data.json().get('msg') page = etree.HTML(response.text) lis = page.xpath('//ul[@class="news-list2"]/li') infors = {} for li in lis: data_id = li.get('d') if data_id not in post_counts: continue w_name = '' elements = li.xpath("div/div[2]/p[1]/a/descendant::text()") for ele in elements: w_name += ele count = post_counts['%s' % (data_id)].split(',')[0] infors.update({'%s' % (w_name): int(count)}) all_accounts.update(infors) #将字典按值降序排序 sorted_accounts = [(k, all_accounts[k]) for k in sorted(all_accounts, key=all_accounts.get, reverse=True)] for k, v in sorted_accounts: print(f'{k} : {v}')
执行结果如下所示:
51Testing软件测试网 : 58 新梦想软件测试 : 34 软件测试培训 : 34 TesterHome : 24 自动化软件测试 : 21 北京尚脑软件测试 : 20 软件测试学园 : 10 自动化测试学堂 : 7 软件测试部落 : 6 性能测试与调优 : 5 Python自动化测试 : 5 glx接口测试 : 2 软件测试相关 : 1
遗留问题
因为搜狗搜索有防爬虫的策略,太频繁的访问会返回待验证码的页面回来造成程序执行失败,可以添加判断,如果有验证码返回通过截图该验证码区域,通过OCR获取后再输入来完成流程,这个步骤还未实现。有兴趣的可以自行扩展