第一篇博客,以爬虫开头,虽然以前也学过爬虫,但是时间比较久,现在又重新捡起,今天谈谈动态网页信息的爬取。
首先介绍一下爬取网页信息的基本思路:1.使用爬虫请求网页,获取网页的源代码 2.解析源代码,在源代码中找到自己想要的信息;3.若还有url地址,再次请求,重复1和2两个步骤。
找到我们所要信息的url,而有些url并不是我们所要信息的真实url,查看源代码时不能找到所要的数据,这是因为这部分信息是通过动态获取的,下面以游民星空的今日推荐为例,获取资讯分类,资讯标题,资讯简介,发布时间,阅读数量,评论数量,图片路径等路径。url='https://www.gamersky.com/news/’
单页信息获取
网页如下
查看网页的源代码,在源代码中找到所要的所有信息:
发现所要的信息都在该代码块中,而评论数那里却没有值,说明评论数是通过动态获取的,找到评论数真实的url.
在network中查看网页的情况,获取评论数的真实连接的
js代码为
其中的joincount就是要获取的评论数,id也就是连接https://cm.gamersky.com/commentapi/count?callback=jQuery18305533735619951856_1541733561188&topic_source_id=1120987%2C1120919%2C1120764%2C1120676%2C1120483%2C1120737%2C1120383%2C1120344%2C1120301%2C1120184%2C1118361%2C1100332%2C1054392&_=1541733562165中的id,因此,每一个评论数真实的url='https://cm.gamersky.com/commentapi/count?callback=jQuery183006329225718227227_1541342483946&topic_source_id=’+id
现在的目的就是获取id,而id从源代码中的data-sid属性中便可以获得。
找到所要获取的信息后,便是请求网页,解析代码了,下面使用lxml解析,其代码如下:
from lxml import etree
import requests
#获取单页的信息
def one_page_info():
url='https://www.gamersky.com/news/'
headers={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
r=requests.get(url,headers=headers)
r_text=r.content.decode('utf-8')
html=etree.HTML(r_text) #解析源代码
#所有信息都在div,属性为Mid2L_con block的li标签中,获取所有li标签
all_li=html.xpath('//div[@class="Mid2L_con block"]/ul[@class="pictxt contentpaging"]/li')
all_inf=[]
#遍历li标签,获取每一个li标签中的信息
for one_li in all_li:
one_type=one_li.xpath('./div[1]/a[1]/text()')[0]#资讯类型
one_name=one_li.xpath('./div[1]/a[2]/text()')[0]#资讯名
one_info=one_li.xpath('string(./div[3]/div[1]/text())')
one_info=str(one_info).replace('\n','')#资讯信息,其中有些信息换行了,将换行符替换
one_time=one_li.xpath('./div[3]/div[2]/div[1]/text()')[0]#发布时间
one_read=one_li.xpath('./div[3]/div[2]/div[2]/text()')[0]#阅读数量
one_id=one_li.xpath('./div[3]/div[2]/div[3]/@data-sid')[0]#获取id
comment_url='https://cm.gamersky.com/commentapi/count?callback=jQuery183006329225718227227_1541342483946&topic_source_id='+one_id
#获取评论数
r_comment=requests.get(comment_url,headers=headers)
r_text=r_comment.text
one_comment=json.loads(r_text[42:-2])['result'][one_id]['joinCount']
one_img=one_li.xpath('./div[2]/a[1]/img/@src')[0]
all_inf.append({"type":one_type,"title":one_name,"info":one_info,"time":one_time,"visited":one_read,"comment":one_comment,"img":one_img})
return all_inf
#保存文件
def save_to_file(all_info):
with open("D:/gamersky.txt",'a',encoding='utf-8') as file:
for o in all_info:
file.write("%s::%s::%s::%s::%s::%s::%s\n"%(o['type'],o['title'],o['time'],o['visited'],o['comment'],o['img'],o['info']))
运行代码后,获得的信息如下:
下面爬取多页的信息
翻页信息获取
步骤还是跟单页一样,当我们翻页的时候,发现网址并没有改变
而查看源代码发现还是第一页的信息,因此多页的时候也是通过动态获取的,查看network找到信息的真正链接。
查看preview在这里找到了信息的真正链接,接下来就是获取链接中的参数,并拼接链接。
对比两页的链接,发现两页链接中只有page参数和最后结尾的数字不同,而最后结尾的数字是代表时间参数。
因此,2页后真正的链接为url=’“https://db2.gamersky.com/LabelJsonpAjax.aspx?callback=jQuery18308577910192677702_1541300651736&jsondata={“type”%3A"updatenodelabel"%2C"isCache"%3Atrue%2C"cacheTime"%3A60%2C"nodeId"%3A"11007"%2C"isNodeId"%3A"true"%2C"page"%3A"+page+"}&_=”+now_time’
将page=1代入也适用,因此,以上是每页想要信息的通用链接(其实,翻页时是将这里的li标签代替了源码中的li标签)。
接下来就是请求各页的网页,得到各页的li标签,然后与单页获取方式一样。
代码:
#爬取多页动态网页,找出url之间的规则,传递所要爬取的页
def more_page_info(page):
now_time=str(time.time()).replace('.','')[0:13]
#每页想要获取信息的url
page_url="https://db2.gamersky.com/LabelJsonpAjax.aspx?callback=jQuery18308577910192677702_1541300651736&jsondata=%7B%22type%22%3A%22updatenodelabel%22%2C%22isCache%22%3Atrue%2C%22cacheTime%22%3A60%2C%22nodeId%22%3A%2211007%22%2C%22isNodeId%22%3A%22true%22%2C%22page%22%3A"+str(page)+"%7D&_="+now_time
headers={
"referer": "https://www.gamersky.com/news/",
"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"
}
r=requests.get(page_url,headers=headers)
html=json.loads(r.text[41:-2])['body']#将json字符串转换为字典
r_text=etree.HTML(html)
all_li=r_text.xpath('//li')#获取所有的li标签
all_inf=[]
for one_li in all_li:
one_type=one_li.xpath('./div[1]/a[1]/text()')[0]
one_name=one_li.xpath('./div[1]/a[2]/text()')[0]
one_info=one_li.xpath('string(./div[3]/div[1]/text())')
one_info=str(one_info).replace('\n','')
one_time=one_li.xpath('./div[3]/div[2]/div[1]/text()')[0]
one_read=one_li.xpath('./div[3]/div[2]/div[2]/text()')[0]
one_id=one_li.xpath('./div[3]/div[2]/div[3]/@data-sid')[0]
comment_url='https://cm.gamersky.com/commentapi/count?callback=jQuery183006329225718227227_1541342483946&topic_source_id='+one_id
#获取评论数
r_comment=requests.get(comment_url,headers=headers)
r_text=r_comment.text
one_comment=json.loads(r_text[42:-2])['result'][one_id]['joinCount']
one_img=one_li.xpath('./div[2]/a[1]/img/@src')[0]
all_inf.append({"type":one_type,"title":one_name,"info":one_info,"time":one_time,"visited":one_read,"comment":one_comment,"img":one_img})
return all_inf
def save_to_file(all_info):
with open("D:/gamersky.txt",'a',encoding='utf-8') as file:
for o in all_info:
file.write("%s::%s::%s::%s::%s::%s::%s\n"%(o['type'],o['title'],o['time'],o['visited'],o['comment'],o['img'],o['info']))
获取前3页信息
for page in range(1,4):
info=more_page_info(page)
save_to_file(info)
print('第%d页下载完成'%page)
总结,要看需要的信息是静态加载还是动态加载的,判断是否是动态网页的简单方式是看在源代码中能否找到想获取的信息。动态网页关键是找到想要信息的真实url,对比每页的url,找出其中的规律并传递参数,若参数是其他动态url获取的,那就要再找参数信息的url,然后按单个信息获取。