爬取动态网页时遇到的问题
准备爬取太平洋网上的小米手机的评论,因为发现评论已经自动打好标签了,并且对于手机的几种性能表现也打了分,以及详细的评论都有,对于后面自己的工作有帮助,所以就准备爬取这些评论.但发现这个网站的每次点下一页都是相同的URL地址,也就是说源代码只显示第一页的评论内容,对于用requests来爬取网页内容,用这个地址的话无法爬取更多内容。后来查了一下,这是用了Ajax动态加载技术,专门用来动态加载网页内容,实现网页的异步更新。
关于Ajax的比较详细的解释推荐两个链接,比较详细的阐述了这是怎么回事,爬取Ajax动态加载和翻页时url不变的网页+网站案例,爬取Ajax动态加载和翻页时url不变的网页。自己说一下我遇到的问题,以及如何既解决的方法,我的问题比较简单就是翻页url地址不变,无法爬取下一页评论的内容。其实这是因为网站的网站隐藏住了url地址后面的参数部分,只显示了地址的主体部分,办法很简单就是找到这些网页url地址被隐藏的参数部分。下面讲一下我的解决步骤:
我爬去的网站是这个小米6x(4GB RAM)点评,当你点开网站点击下一页,再看看对应的url地址都是一样的没有变化,再看看对应的源代码发现都是一样的,只显示第一页的评论内容,第2,3,4....内容无法找到。开始找网站被隐藏的url参数部分。我用的浏览器时谷歌Chrome浏览器,按F12打开开发者选项,打开找到最上面导航栏network,下面选择All,筛选网页文件类型,这里也可选XHR表示动态网页类型,选择右边的preview来展示网页的内容用来确定我们要找的网页,
我们点击最左侧网页中的下一页就会在边上的对应文件中找到这个网页的代码文件信息,通过preview可以看见这个文件的预览。当我们找到要找的网页时,在点击headers找到我们所要的信息。
Query String Parameters显示了我们请求的网页地址的参数部分,也就是我们网页的主体部分是'http://product.pconline.com.cn/readIntf.jsp?'参数部分是'url:http://pdcmt.pconline.com.cn/front/2015/mtp-list.jsp?productId=1073867&filterBy=-1&itemCfgId=-1&order=2&pageNo=2&vId=432764',我们打来的网页的完整url地址是 'http://product.pconline.com.cn/readIntf.jsp?url=http%3A%2F%2Fpdcmt.pconline.com.cn%2Ffront%2F2015%2Fmtp-list.jsp%3FproductId%3D1073867%26filterBy%3D-1%26itemCfgId%3D-1%26order%3D2%26pageNo%3D2%26vId%3D432764'。标黄部分就是参数部分。再打开几页评论发现,参数部分的pageNo=1,2,3,4,5...分别代表评论的1,2,3,4.。。页,也就是说我们找到下一页评论网站的页码,改变这个值,就可以进入到不同页面的评论页面了。在编码时为了方便用get(url,params)直接得到完整的url地址,params是参数的字典表示即params={'url':'http://pdcmt.pconline.com.cn/front/2015/mtp-list.jsp?productId=1073867&filterBy=-1&itemCfgId=-1&order=2&pageNo=2&vId=432764'},这时候发现改动pageNo比较困难,所以尝试用这个参数地址试试能不能打开评论页面,结果发现可以打开,只要改动页面值,就可跳到相应的评论页,同样,按F12,找到这个页面对应的Query String Parameters.
可以看见,Query String Parameters.对应是个字典,也可以通过键值对的形式改变字典pageNo的值,来达到访问不同评论网址的目的。下面就是爬取网站内容的工作了,这就是我找隐藏网址的过程。
总结一下,我们就是在找动态网页的时候通过打开开发者选项(F12),找到要爬取网页文件的header,在Query String Parameters中找到对应的参数部分,最后将url主体部分和参数部分结合一起就能得到完整的url地址了。
下面附一下爬取的代码:
1 from bs4 import BeautifulSoup 2 import requests 3 import re 4 import pandas as pd 5 6 #太平洋网爬取小米6X的评论 7 #动态网页爬取(ajax) 8 9 10 def getHtml(url,data): #只输入URL的主体部分,后面的参数用下面的字典附加上 11 try: 12 r=requests.get(url,params=data) 13 r.raise_for_status() 14 r.encoding=r.apparent_encoding 15 return r.text 16 except: 17 print('爬取失败') 18 19 def getComment(html):#获得一页的评论 20 commentList=[] 21 soup=BeautifulSoup(html,'html.parser') 22 lines=soup.find_all('dl',attrs={'class':'cmt-content'})#获得一整页所有的评论总的标签内容 23 for line in lines:#对每个评论进行解析,line就是每个评论的总标签内容<di class=cmt_content...> ...</dl> 24 goal=line.find('strong',attrs={'class':'goal'}).string#得到总评分 25 comm_totall=line.find('div', attrs={'class':'eval-star'}).p.string.strip()#总评价 26 catagory=line.find('ul',attrs={'class':'goal-detail'}).find_all('li')#获得几个属性的评价 27 # print(catagory) 28 a1=catagory[0].string 29 a2 = catagory[1].string 30 a3 = catagory[2].string 31 a4 = catagory[3].string 32 a5 = catagory[4].string 33 comm_detail=line.find('p',attrs={'class':"text"})#具体评价,但这部分内容存在标签与字混合的成分,要把标签替换掉 34 detail_new=re.sub(r'<.*?>','',str(comm_detail))#因为部分内容存在空格\xa0,要去掉这部分空格的代码显示 35 detail=','.join(detail_new.split())#用逗号来将分割的字符串两节起来,join()函数用来连接字符串数组元素 36 commentList.append([goal,comm_totall,a1,a2,a3,a4,a5,detail]) 37 return commentList 38 # print(commentList) 39 40 def comment(url,num):#获得多个页面的评论 41 data={'productId': 1073867, 42 'filterBy': -1, 43 'itemCfgId': -1, 44 'order': 2, 45 'pageNo': 1, 46 'vId': 432764} 47 comment_all=[] 48 for i in range(1,num+1): 49 data['pageNo']=i 50 html=getHtml(url,data) 51 comment=getComment(html) 52 comment_all+=comment 53 print('页数',i) 54 #print(comment_all) 55 return comment_all 56 57 if __name__=='__main__': 58 url='http://pdcmt.pconline.com.cn/front/2015/mtp-list.jsp?' 59 a=comment(url,17) 60 print(len(a)) 61 name = ['总评分', '总评价','性价比','屏幕','流畅度','电池','相机','细评'] 62 test = pd.DataFrame(columns=name, data=a) 63 test.to_csv('D:/mi6x.csv', index=False) # 去掉默认的行索引index