Python爬蟲——使用 lxml 解析器爬取汽車之家二手車信息

本次爬蟲的目標是汽車之家的二手車銷售信息,範圍是全國,不過很惋惜,汽車之家只顯示100頁信息,每頁48條,也就是說最多隻可以爬取4800條信息。html

因爲此次爬蟲的主要目的是使用lxml解析器,因此在信息的查找上面徹底只會涉及lxml中選擇器的用法,雖然lxml能夠同時使用CSS選擇器和Xpath選擇器,可是爲了更加突出效果,暫且只使用Xpath。python

爬蟲老套路,分爲3個步驟:git

  1. 分析網頁信息構成,找到切入點
  2. 獲取網頁,提取有效信息
  3. 儲存信息

網頁分析

網頁結構分析的通常思路是先找到第一個須要爬取的連接,而後看看後面的連接是以什麼方式構成的,進而選擇一種方式全量爬取(通常使用循環或者遞歸的方式)。github

找到切入點——第一個 URL

經過查看連接構成的規律很容易能夠發現,汽車之家二手車的信息是由不少選擇項來構成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中存儲的數據:

mongodb

爬蟲源碼

爬蟲比較簡單,爬取的信息也沒有多大的價值,所以不作後續深刻研究,這個爬蟲主要是爲了介紹lxml解析器和Xpath選擇器。

源碼:https://github.com/Hopetree/Spiders100/blob/master/autohome.py

相關文章
相關標籤/搜索