近期社会广泛关注武汉肺炎疫情,各类网传、听说类谣言信息误导公众,引发社会恐慌。而腾讯较真查证平台就是将网络上的谣言进行收集并解答,而今天我们要做的就是抓取该平台上的谣言信息。
我们使用借助浏览器的开发者工具来分析请求。首先使用Chrome浏览器打开较真查询平台网站https://vp.fact.qq.com/home ,然后在页面中点击鼠标右键,从弹出的快捷菜单中选择检查选项,此时便可以弹出开发这工具。
滚轮下拉刷新数据,可以看到下面的数据请求:
排除其中type是jpeg png这样的图片文件,剩下一条就是我们需要的数据,选择第一条然后单击在右边的选择Preview可以看到就是我们需要的数据:
针对于每一条请求都可以在开发者工具中查看他们的Headers与Response。而针对于较真查证平台第一条请求就是我们需要的,现在我们来分析这个请求的headers:
其中可以看到该请求是一个GET请求,url是https://vp.fact.qq.com/loadmore 而参数有四个artnum page _ 以及callback。这里我们不断下拉来得到新的请求数据,来观察这四个参数的值。得到一下结论:
我们通过开发者工具来查看这条请求的相应:
可以看到这是一个像json的字符串,不过由jsonp1()包括,只要我们删除了这几个字符就可以当作json格式了。
这里我们使用python的requests库来编写程序,使用sqlite3来保存抓取到的数据,规划步骤如下:
import requests
import time
import json
# 请求网址
url = "https://vp.fact.qq.com/loadmore"
# 请求参数
params = {
'artnum':0,
'page':0,
'_':1581846620972,
'callback':'jsonp1',
}
# 构建请求头
headers = {
'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Mobile Safari/537.36',
'referer': 'https://vp.fact.qq.com/home'
}
# 获得数据
def getMessage(page):
params['_'] = int(round(time.time()*1000))
params['page'] = page
req = requests.get(url,headers=headers,params=params).text
return json.loads(req[7:-1])['content']
# 测试下可以程序可以看到没有问题
print(getMessage(0)[0])
通过getMessage()函数得到的是一个list对象,其中每一条都是一个字典对象存储了谣言相关的信息,包括:title、author、authordesc、id等信息。我们创建在本地创建一个sqlite3数据库并且添加yiqing数据表:
PS E:\credher\gitee\spider\较真谣言> sqlite3 jiaozhen.db
SQLite version 3.30.1 2019-10-10 20:19:45
Enter ".help" for usage hints.
sqlite> create table yiqing(abstract text,arttype text,author text,authordesc text
,cover text,coverrect text,coversqual text,date text,explain text,id text,iscolled
text,markstyle text,result text,section text,tag text,title text,type text,videou
rl text);
sqlite> .table
yiqing
import sqlite3
# 循环获取数据并写入数据库
def writeToSql(conn):
page = 0
while True:
results = getMessage(page)
if len(results) == 0:
break
for result in results:
tags = ""
for tag in result['tag']:
tags = tags + tag
conn.execute('insert into yiqing values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',(result['abstract'],result['arttype'],result['author'],result['authordesc'],result['cover'],result['coverrect'],result['coversqual'],result['date'],result['explain'],result['id'],result['iscolled'],result['markstyle'],result['result'],result['section'],tags,result['title'],result['type'],result['videourl']))
conn.commit()
print(page)
page = page + 1
conn.close()
# 运行就好了
# conn = sqlite3.connect('jiaozhen.db')
# writeToSql()
这样所有关于疫情的辟谣数据就完成了抓取:
sqlite> select count() from yiqing;
320
可看到截至到2月16日一共有320条数据。如果此时你要更高的需求想要抓取较真平台上更多的数据,这是我们就需要使用他的搜索工具了,接下来来完成这部分的数据抓取。
与上面的分析一样,以搜索癌症为例,在搜索后在开发者工具的Network中找到对应的请求数据:
可以看到请求的URL是https://vp.fact.qq.com/searchresult 而参数如下:
其结果依然是一个类似的json字符串,不过依然需要切片删除jsop1(),这里面的total决定了你请求中的num的最大值,其数据保存在content中,一共有20条。
由于搜索需要相应的关键词,可以自行构建,这里我们使用较真平台中大家都在查这些中出现的关键词作为搜索依据,依然根据上面的分析过程可以知道这部分关键词从https://vp.fact.qq.com/searchPage?ADTAG=&_=1581854494858&callback=jsonp1 中获取,这个请求的响应是一段被jsop1()包括的html字符串:
jsonp1("\u003Cdiv id=\"search\">\n \u003Cdiv class=\"top\">\n \u003Cimg src=\"//mat1.gtimg.com/www/coral/jiaozhen/imgs/toplogo_small.png\" class=\"top_logo\" id=\"search_top_logo\">\n \u003Cdiv class=\"top_search_div\">\u003Cform id=\"search_form\" action=\"\">\u003Cinput class=\"top_search\" id=\"top_search\" autocomplete=\"off\" type=\"search\" placeholder=\" 输入关键词查一查 \" autofocus=\"autofocus\"/>\u003C/form>\u003C/div>\n \u003Cimg src=\"//mat1.gtimg.com/www/coral/jiaozhen/imgs/close.png\" id=\"top_search_cancle\">\n \u003Cspan class=\"top_cancel\" id=\"top_cancel\">取消\u003C/span>\n \u003Cbutton id=\"btn\" style=\"display: none\">\u003C/button>\n \u003C/div>\u003Cdiv class=\"content\">\n\n \u003Cdiv class=\"content_title\" id=\"content_title\">大家都在查这些:\u003C/div>\n \u003Cdiv class=\"content_list\" id=\"content_list\">\n \u003Cul>\n \u003Cli class=\"hot_search_li\">\n \u003Cdiv class=\"content_text\">武汉病毒所女研究生黄燕玲是新冠肺炎零号病人\u003C/div>\n \u003C/li>\n \u003Cli class=\"hot_search_li\">\n \u003Cdiv class=\"content_text\">2月17日起至疫情防控结束全国高速所有车免费通行\u003C/div>\n
不过其中的小于号使用的是\uxxxx这样的字符串,如果想要转换为正常的字符串需要对其解码。然后就是一串HTML字符串了可以使用任何方式来进行数据提取。
现在我们来编写代码,其具体步骤如下:
from bs4 import BeautifulSoup
import re
def getKeywords():
content = requests.get("https://vp.fact.qq.com/searchPage?ADTAG=&_=1581854494858&callback=jsonp1").text
# 将\uxxxx解码为相应的字符
content = re.sub(r'(\\u[\s\S]{4})',lambda x:x.group(1).encode("utf-8").decode("unicode-escape"),content)
# 使用bs4来获取html中数据
soup = BeautifulSoup(content)
results = soup.find_all(class_='\\"content_text\\"')
keywords = []
for result in results:
keywords.append(result.string)
return keywords
searchURL = "https://vp.fact.qq.com/searchresult"
searchParams = {
'title':'癌症',
'num':0,
'_':1581854497987,
'callback':'jsonp1',
}
def getSearchMessage(keyword):
searchParams['title'] = keyword
searchParams['num'] = 0
req = requests.get(searchURL,params=searchParams,headers=headers).text
total = json.loads(req[7:-1])['total']
content = json.loads(req[7:-1])['content']
results = []
for result in content:
results.append(result['_source'])
for num in range(1,int(total/20)+1):
searchParams['num'] = num
req = requests.get(searchURL,params=searchParams,headers=headers).text
content = json.loads(req[7:-1])['content']
for result in content:
results.append(result['_source'])
return results
在jiaozhen.db中创建表yaoyan:
create table yaoyan(id text primary key not null,title text,result text,source text,oriurl text,abstract text,cover text,updatedAt text,date text,author text,authordesc text);
def writeSearchToSql(conn):
# 获取关键字列表,你也可以自行构建keywords
keywords = getKeywords()
for keyword in keywords:
print(keyword)
results = getSearchMessage(keyword)
for result in results:
try:
if 'oriurl' not in result.keys():
result['oriurl'] = ''
if 'cover' not in result.keys():
result['cover'] = ''
if 'source' not in result.keys():
result['source'] = ''
conn.execute('insert into yaoyan values(?,?,?,?,?,?,?,?,?,?,?)',(result['id'],result['title'],result['result'],result['source'],result['oriurl'],result['abstract'],result['cover'],result['updatedAt'],result['date'],result['author'],result['authordesc']))
conn.commit()
except sqlite3.IntegrityError:
print('Error %s已经存在于数据库中'%result['id'])
conn.close()
# 取消注释运行就可以了
# conn = sqlite3.connect('jiaozhen.db')
# writeSearchToSql(conn)
# print('ok')
这样我们就得到了较真中存在的谣言相关数据,其分别保存在yiqin和yaoyan两个表中,我们可以在sqlite3中查询结果:
sqlite> select count() from yiqing;
320
sqlite> select count() from yaoyan;
4898
其中搜索得到的数据差不多有近5000条,这并不是全部,如果想要获取更多的数据就需要自己构建writeSearchToSql()函数中的keywords参数就可以了。