python 爬虫实例详细介绍之爬取大众点评的数据
一.
Python作为一种语法简洁、面向对象的解释性语言,其便捷性、容易上手性受到众多程序员的青睐,基于python的包也越来越多,使得python能够帮助我们实现越来越多的功能。本文主要介绍如何利用python进行网站数据的抓取工作。我看到过利用c++和Java进行爬虫的代码,c++的代码很复杂,而且可读性、可理解性较低,不易上手,一般是那些高手用来写着玩加深对c++的理解的,这条路目前对我们不通。Java的可读性还可以,就是代码冗余比较多,同样的一个爬虫,java的代码量可能是python的两倍,感觉也没有python容易上手。因此,建议大家以后如果对爬虫有兴趣的话直接使用python就好。
二.本文首先爬取大众点评-北京的火锅这个条目下的部分数据。下面,我主要针对如何爬取数据进行讲解,针对数据进行分析的部分就略过。会根据我自己的理解和经验对代码进行详细的分析,比较适合初学者,高手请出门右转。由于是针对初学者,所以我最大程度地将代码进行精简,当然也因此删去了一些功能。
三、注意!
不要盲目的直接把代码复制直接运行,最好先看完本文,然后再运行。因为我是在ubuntu14.04下运行的我的代码,因此在获取数据时的编码格式不一样,输出信息到窗口时的编码也会有所不同,在Linux下默认编码是utf-8,而在windows下默认编码是gbk,所以,如果系统不同,直接运行代码,可能会输出乱码,这不代表我的代码有问题。需要注意的问题,在本文中我基本上都给了讲解,如果还有问题的话,欢迎留言探讨。
本人的浏览器为forefox,不同浏览器的查看元素的方法和界面也会有所不同,我在下文进行介绍时,只能以我的浏览器为准,不同的浏览器可以自己找一下相应的东西,一般不会差太多。
四.闲话少叙,直接上代码。
[python] view plain copy
# -*- 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) 。这种情况下可以加如下几行代码:
[python] view plain copy
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是我的浏览器信息,在使用时需要根据自己的电脑进行修改。请求头中有些数据是不需要的,因此我只选择了几条。
本文出自 “李世龙” 博客,谢绝转载!
原文地址:http://lwm666.blog.51cto.com/11811339/1946329