python2.7爬虫实例详细介绍之爬取大众点评的数据

发表于:2021-6-24 09:48

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:勤奋的清风    来源:CSDN

  一.Python作为一种语法简洁、面向对象的解释性语言,其便捷性、容易上手性受到众多程序员的青睐,基于python的包也越来越多,使得python能够帮助我们实现越来越多的功能。本文主要介绍如何利用python进行网站数据的抓取工作。我看到过利用c++和java进行爬虫的代码,c++的代码很复杂,而且可读性、可理解性较低,不易上手,一般是那些高手用来写着玩加深对c++的理解的,这条路目前对我们不通。Java的可读性还可以,就是代码冗余比较多,同样的一个爬虫,java的代码量可能是python的两倍,感觉也没有python容易上手。因此,建议大家以后如果对爬虫有兴趣的话直接使用python就好。
  二.本文首先爬取大众点评-北京的火锅这个条目下的部分数据。下面,我主要针对如何爬取数据进行讲解,针对数据进行分析的部分就略过。会根据我自己的理解和经验对代码进行详细的分析,比较适合初学者,高手请出门右转。由于是针对初学者,所以我最大程度地将代码进行精简,当然也因此删去了一些功能。
  三、注意!
  不要盲目的直接把代码复制直接运行,最好先看完本文,然后再运行。因为我是在ubuntu14.04下运行的我的代码,因此在获取数据时的编码格式不一样,输出信息到窗口时的编码也会有所不同,在linux下默认编码是utf-8,而在windows下默认编码是gbk,所以,如果系统不同,直接运行代码,可能会输出乱码,这不代表我的代码有问题。需要注意的问题,在本文中我基本上都给了讲解,如果还有问题的话,欢迎留言探讨。
  本人的浏览器为forefox,不同浏览器的查看元素的方法和界面也会有所不同,我在下文进行介绍时,只能以我的浏览器为准,不同的浏览器可以自己找一下相应的东西,一般不会差太多。
  四.闲话少叙,直接上代码。
  # -*- coding:utf-8 -*-
  import re
  from bs4 import BeautifulSoup
  import json
  import threading
  from requests import Session
  class dazp_bj:
  def __init__(self,category):
  self.baseUrl='http://www.dianping.com'
  self.bgurl=category[0]
  self.typename=category[1]
  self.page=1
  self.pagenum=10 #设置最大页面数目,大众点评每个条目下最多有50页,可以根据自己需求进行设置
  self.headers={
  "Host":"www.dianping.com",
  "User-Agent":"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:48.0) Gecko/20100101 Firefox/48.0",
  #此User-Agent是我本人的参数,在使用时记得修改为自己的参数,如何获取下四.4部分有讲解
  "Referer":"http://www.dianping.com/beijing",
  }
  def start(self):
  self.s=Session()#定义一个Session()对象
  print self.bgurl,self.typename
  print "please wait for 15"
  dazp_bj.__parseHtml(self,self.bgurl) #调用__parseHtml函数
  print 'getdata down'
  def __parseHtml(self,preurl):
  _json=dict()#定义一个字典用以存储数
  html=self.s.post(preurl,headers=self.headers).text#发送请求,获取html
  soup=BeautifulSoup(html,'lxml') #进行解析
                name=['商家名称','评论数量','人均消费','地址','评分','链接']
  for li in soup.find('div',class_="shop-wrap").find('div',id="shop-all-list").ul.find_all('li'):
  info=li.find('div',class_='txt')
  _json[name[0]]=info.find('div',class_='tit').a.h4.get_text().encode('utf-8')
  _json[name[1]]=int(info.find('div',class_='comment').find('a',class_="review-num").b.get_text().encode('utf-8'))
  _json[name[2]]=int(re.sub('¥','',info.find('div',class_='comment').find('a',class_="mean-price").b.get_text().encode('utf-8')))
  _json[name[3]]=info.find('div',class_='tag-addr').find('span',class_='tag').get_text().encode('utf-8')+info.find('div',class_='tag-addr').find('span',class_='addr').get_text().encode('utf-8')
  _json[name[4]]=float(info.find('span',class_='comment-list').find_all('b')[0].get_text())+float(info.find('span',class_='comment-list').find_all('b')[1].get_text())+float(info.find('span',class_='comment-list').find_all('b')[2].get_text())
  _json[name[5]]=self.baseUrl+info.find('div',class_='tit').a['href']
  with open(self.typename+'.json','a') as outfile:
  json.dump(_json,outfile,ensure_ascii=False)
  with open(self.typename+'.json','a') as outfile:
  outfile.write(',\n')
  self.page+=1
  if self.page<=self.pagenum:
  self.nexturl=self.baseUrl+soup.find('div',class_='page').find('a',class_='next')['href']  #获得下一页的链接
  dazp_bj.__parseHtml(self,self.nexturl)
  if __name__=='__main__':
  cat=[(r'http://www.dianping.com/search/category/2/10/g110',u'火锅 ')]
  obj=list()
  obj.append(dazp_bj(cat[0]))
  [threading.Thread(target=foo.start(),args=()).start for foo in obj]#多线程执行obj列表中的任务
  五.上面是所有代码。接下来则对代码进行精细的讲解。
  1.# -*- coding:utf-8 -*- #这句代码是在文件的开头设置好编码的格式,这样就能够在文件里写中文,否则python2.7会默认使用ASCII编码。个人建议最好在所有的.py文件的开头都写上这句代码,防止不必要的错误。有时候这样设置后在爬取某些网站时仍然会出现问题,比如说会报这样的错误:UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128) 。这种情况下可以加如下几行代码:
  import sys
  reload(sys)
  sys.setdefaultencoding("utf-8" )
  暂时不知道为什么会这样,但是这样就可以解决问题了。据说python2.7在这方面特别容易出错,在python3中则会好很多,可能以后会转到python3吧,差别应该不大,新手可以直接学3.
  2.现在讲一下引用的包。re是正则表达式,对网页进行解析时很有用,非常强大,当你掌握了正则表达式的使用后,你就可以在各个网页中来回驰骋了;json是一种数据格式,我利用json文件在保存爬取到的字典数据,感觉json保存字典数据很强大,调用读取都很便捷。之前看到一个txt文件保存的字典数据,在读数据的时候很麻烦,没有json便捷;threading 是多线程,可以提高爬取效率,本文中因为只抓了一个链接,因此没有用到它的功能,在cat中多加入几个链接,并append到obj中就可以使用这个功能;bs4是python的一个很强大的对网页进行解析的包,如果没有这个包,如果要解析数据都是要使用正则表达式的,那个场面一定很惨烈。python也提供了另一种方式方便查找网页中的标签:xpath,只用过一次,配合firefox的firebug很好用,在定位到感兴趣的标签后直接右键即可获得该标签的xpath路径,省得我们还要一个一个标签去找,但是当我们爬取的网站比较大、复杂的时候,这种方式可能就不是那么适用了,因为不同条目的同一种类的数据可能在不同的位置,或者有些数据没有值的情况下这个标签就不存在,这个时候只能使用beautifulsoup来根据id、class、name等值准确定位到标签;requests是向服务器发送请求的包,有post和get两张方法,可以传入很多参数,如headers,cookies,data,params等,headers是你浏览器的请求头数据,传入这个参数可以将你的爬取访问伪装成浏览器的正常访问,对一些网站有效,有些网站则需要传入cookies,data和params是在其他情况下需要传入的参数,初学者一般用不到,在此就不赘述,需要的时候百度基本上可以找到方法。requests下的Session()方法和request有点相似,也包括get和post方法,传的参数也基本上差不多,但是会话对象requests.Session能够跨请求地保持某些参数,比如cookies,即在同一个Session实例发出的所有请求都保持同一个cookies,而requests模块每次会自动处理cookies,这样就很方便地处理登录时的cookies问题。但是本文中貌似不需要使用session,只是习惯性的使用这个,大家可以修改后试一下:将后文的self.s替换为requests .
  3.首先定义了一个类,类下面有很多方法。__init__是对类的参数进行初始化,类内的全局变量都可以在此设置,在其他地方设置也可,不过要在变量名前加上global。start(),类中的方法和函数都在此进行调用,start可以说是类的入口。__parseHtml是定义的爬取数据的主函数。
  4.下面介绍如何针对每个数据条目定制解析语句,以及如何获得headers参数。
  打开页面之后右键然后选择查看元素(有些浏览器是检查元素,我使用的是firefox,注意不是查看源代码)然后点网络(Network),然后点左上角“所有”旁边的删除符号,然后点击重新载入(或者刷新页面),可以看到下图所示的情况:
  点击最上面的那一条,得到下图,请求头信息、cookie、参数等信息都在这里。上面代码中的headers内的User-Agent是我的浏览器信息,在使用时需要根据自己的电脑进行修改。请求头中有些数据是不需要的,因此我只选择了几条。
  解析每个条目数据基本上也是右键查看元素就可以搞定。
  右键时直接在那个条目的位置右键,即可直接定位到该标签的位置,不需要F12后一层一层找浪费时间。
  举个例子:假如我们要查找“井各老灶火锅(望京新世界店)”这几个字,我们可以将鼠标放在这些字上方,然后点击右键,选择查看元素。如下图所示,可以看到该标签以及该标签所属的每一层的标签属性。如果要利用BeautifulSoup定位到这个标签,可以从最上面的标签一级一级找下来,但是这样不仅耗费时间增加代码量,而且毫无必要,一般来说只需要找到包含它的标签中的具有唯一标志性的标签即可。对于这个标签,我们首先找到每一个店铺所在的标签,即:
  soup.find('div',class_="shop-wrap").find('div',id="shop-all-list").ul.find_all('li')
  从上面这句代码可以看出,尽管li的上面后很多种各种各样的标签,但是我在定位的时候可以跳过这些对我无用的标签。各个条目的数据都可以按照这种方法来定位并获取。
  5.运行。在windows下运行时,打开cmd窗口:然后输入python并空格,然后将需要运行的文件拖到窗口中回车即可运行。
  最后得到的数据是长这样子的:
  {"商家名称": "珍滋味港式粥火锅(工体店)", "评分": 27.0, "地址": "火锅工人体育场东路丙2号中国红街3号楼2层里", "链接": "http://www.dianping.com/shop/6232395", "人均消费": 174, "评论数量": 2307}
  {"商家名称": "井格老灶火锅(望京新世界店)", "评分": 26.200000000000003, "地址": "火锅望京广顺南大街路16号新世界利莹百货购物中心1层望京南地铁A口西北口往索尼大厦50-100米", "链接": "http://www.dianping.com/shop/23721600", "人均消费": 105, "评论数量": 1387}
  六、拓展
  1.有些网站有反爬虫措施,当爬虫访问网站的频率过高时,会需要输入验证码,对于一般的爬虫来说,专门设计一个自动识别验证码并填写的模块没有必要,而且对于初学者来说也有点难度。最简单粗暴的方法就是在每次访问完一个页面后加入这样一句代码,人为降低访问频率:
  import time
  import random
  time.sleep(1)#停顿一秒
  #或者这样
  time.sleep(random.uniform(1,4))#表示设置一个随机数作为停顿时间,为1~4之间的一个数.
  2.有些网站需要加载动态页面,建议使用selenium包,也可配合PhantomJS(模拟浏览器,不是python的包,在需要鼠标点击等操作时用起来很便捷)一起使用。对于这个包的使用方法网上有大致的介绍,可以参考一下,或许以后我也会单另再写一篇文章来介绍~
  3.Python实现爬虫功能的包还有很多,比如urllib、urllib2等,这些包比较基础,也比较简单,但是没有requests好用、强大,大部分爬虫都可以用requests和bs4的搭配组合来完成。
  4.还有一个爬虫的包是scrapy。这个包用起来稍微复杂一些,但是上手后很便捷,很多代码不用自己去写,数据间的传递等工作它都可以自动完成,而不需要手动去操作。在熟练使用requests和bs4等包后,对于爬虫原理和方法有了一定的了解后再去使用这个包可能会好一些。

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号