本次爬虫的目标是汽车之家的二手车销售信息,范围是全国,不过很惋惜,汽车之家只显示100页信息,每页48条,也就是说最多只可以爬取4800条信息。html
因为此次爬虫的主要目的是使用lxml解析器,因此在信息的查找上面彻底只会涉及lxml中选择器的用法,虽然lxml能够同时使用CSS选择器和Xpath选择器,可是为了更加突出效果,暂且只使用Xpath。python
爬虫老套路,分为3个步骤:git
网页结构分析的通常思路是先找到第一个须要爬取的连接,而后看看后面的连接是以什么方式构成的,进而选择一种方式全量爬取(通常使用循环或者递归的方式)。github
经过查看连接构成的规律很容易能够发现,汽车之家二手车的信息是由不少选择项来构成URL的,好比按照品牌,或者车系价格城市等等,若是选择清空筛选,那就获得了全国二手车信息的URL,这正是我须要的,URL为http://www.che168.com/china/list/。mongodb
第一页的连接很容易获得,而后能够看到,二手车的信息总计只有100页,每页48个信息,也就是总共有4800个信息能够爬取到。数据库
经过点击“下一页”能够直接看到第2页的连接大概是http://www.che168.com/china/a0_0msdgscncgpi1ltocsp2exb1x0/,继续点击下一页,查看第3页、第4页的连接http://www.che168.com/china/a0_0msdgscncgpi1ltocsp3exb1x0/能够看到,规律很明显,每页的连接构成除了页码中的数字不一样外,其余部分彻底同样。iphone
经过上面的分析,构造全部100页的连接是件很简单的事情,只须要把连接中的数字部分循环替换一下就好了,这就是循环的方式了。这个方式对付这种连接颇有规律的URL在适合不过了,具体参考代码:ide
for i in range(1,101): url = 'http://www.che168.com/china/a0_0msdgscncgpi1ltocsp{}exb1x0/'.format(i)
不过,为了更好的适应更加多变的URL,我更加倾向于使用递归的方式来爬取下一页的信息。所以本篇爬虫中不适用上面这种循环爬取的方式,转而使用递归爬取。函数
所谓递归,首先找到一个递归的出口,也就是爬虫的终点。对于这个爬虫,终点就是当爬到第100页的时候就要结束,既然思路明确了,那能够看看第100页与其余页面有什么不一样。工具
经过分析,能够看到1-99页都有一个“下一页”的按钮,而最后一页是没有这个按钮的,这就是出口。只须要设置一个判断就好了:
def get_items(self,url): html = requests.get(url,headers=self.headers).text selector = etree.HTML(html) next_page = selector.xpath('//*[@id="listpagination"]/a[last()]/@href')[0] next_text = selector.xpath('//*[@id="listpagination"]/a[last()]/text()')[0] url_list = selector.xpath('//*[@id="viewlist_ul"]/li/a/@href') for each in url_list: the_url = 'http://www.che168.com'+each self.get_infos(the_url) if next_text == '下一页': next_url = 'http://www.che168.com/china'+next_page self.get_items(next_url)
上面这段代码主要包含下面几个步骤:
html = requests.get(url,headers=self.headers).text
这一句是经过requests来获取网页结构,造成标签树。
selector = etree.HTML(html) next_page = selector.xpath('//*[@id="listpagination"]/a[last()]/@href')[0] next_text = selector.xpath('//*[@id="listpagination"]/a[last()]/text()')[0]
上面3句的用意是使用lxml解析网页,而后使用xpath选择器找到下一页的连接,同时尝试找到“下一页”这3个字。
每当找到“下一页”这个按钮,就执行if下面的代码,也就是把找到的下一页连接放入函数中去继续执行,这就造成了递归。固然,前面也说过了,只有1-99页是有这个按钮的,因此到了第100页就找不到这3个字了,这里的if判断就会中止执行, 递归也就结束了。
这一段就是递归的判断:
if next_text == '下一页': next_url = 'http://www.che168.com/china'+next_page self.get_items(next_url)
经过查看页面就能看出来,每一个页面有48个二手车信息,可是因为页面信息太少了,因此最好再把每一个二手车的主页面打开,因此须要先提取到每一个二手车的主页面的连接
这段代码就是提取每一个页面的全部二手车连接,而且对每一个连接执行函数去提取有效信息:
url_list = selector.xpath('//*[@id="viewlist_ul"]/li/a/@href') for each in url_list: the_url = 'http://www.che168.com'+each self.get_infos(the_url)
能够看到,提取页面中二手车信息的代码是封装到了一个函数中,而这个函数须要传入一个参数,那就是相应的二手车主页URL。
每一个主页是一个单独的连接,因此能够写一个函数,传入一个url,而后输出须要提取的信息就好了,具体代码以下:
def get_infos(self,page_url): dic = {} html = requests.get(page_url,headers=self.headers).text selector = etree.HTML(html) car_info = selector.xpath('//div[@class="car-info"]') if car_info: dic['title'] = car_info[0].xpath('//div[@class="car-title"]/h2/text()')[0] price = car_info[0].xpath('//div[@class="car-price"]/ins/text()')[0] dic['price'] = price.strip(' ').replace('¥','') dic['xslc'] = car_info[0].xpath('//div[@class="details"]/ul/li/span/text()')[0] dic['scsp'] = car_info[0].xpath('//div[@class="details"]/ul/li/span/text()')[1] dic['dwpl'] = car_info[0].xpath('//div[@class="details"]/ul/li/span/text()')[2] dic['city'] = car_info[0].xpath('//div[@class="details"]/ul/li/span/text()')[3] dic['call_num'] = car_info[0].xpath("//a[contains(@class,'btn') and contains(@class,'btn-iphone3')]/text()")[0] commitment_tag = car_info[0].xpath('//div[@class="commitment-tag"]/ul/li/span/text()') dic['commitment_tag'] = '/'.join(commitment_tag) dic['address'] = car_info[0].xpath('//div[@class="car-address"]/text()')[0].strip() dic['call_man'] = car_info[0].xpath('//div[@class="car-address"]/text()')[-1].strip() print(dic) self.coll.insert(dic)
在这段代码中,首先建立一个空的字典,而后为了后续存储信息更加方便,所以把全部的信息都放到一个字典中去。
提取的方式依然是使用xpath选择器,因为有的信息格式不符合以后要保存的格式,因此使用python的基本方法稍微处理了一下。
最后,再保存信息以前,只用print打印一下提取到信息,查看信息的完整性和准确性。
这样,一个爬虫的前2步就已经完成了,剩下一的就是选中一个合适的方式将信息储存起来。数据库是个好工具,mongodb更是一个好数据库,没错,就是你了!
因为爬虫的信息不须要太明确的关系,主要目的是存储信息,因此数据库的选择上优先选择mongodb,这种非关系型数据库真是最好不过了。
首先须要导入相应的数据库工具库
from pymongo import MongoClient
而后是链接数据库,因为这个爬虫是写到一个AutohomeSpider类中,所以能够在初始化的时候直接连接指定的数据库,而且能够同时建立表格。
具体代码以下:
self.coon = MongoClient('localhost',27017) self.coll = self.coon['autohome']['Oldcars']
上述代码能够看到,链接了本地mongodb以后,能够直接建立以前不存在的数据库和数据表。
mongodb插入信息的方式很是简单,只须要将数据存放到一个字典中,而后使用 insert()
方法就行。
具体插入信息的代码在上面代码中的
self.coll.insert(dic)
也就是每爬取一条信息就存入mongodb中。
最后爬虫结束,可使用第三方可视化工具查看一下mongodb中存储的数据:
爬虫比较简单,爬取的信息也没有多大的价值,所以不作后续深刻研究,这个爬虫主要是为了介绍lxml解析器和Xpath选择器。
源码:https://github.com/Hopetree/Spiders100/blob/master/autohome.py