使用Python爬取易車網(wǎng)的車型數(shù)據(jù)

今天跟大家分享下最近研究的python爬取易車網(wǎng)的車型數(shù)據(jù). 本來app上有現(xiàn)成的api來獲取,但是沒有停售的車型數(shù)據(jù),所以只好現(xiàn)學現(xiàn)用了.

先上代碼傳送門

思路

品牌:
關閉Chrome的JavaScript支持,打開車型頁發(fā)現(xiàn)品牌數(shù)據(jù)并沒有加載出來,由此可以斷定品牌數(shù)據(jù)是通過JavaScript動態(tài)加載出來的,打開Chrome的開發(fā)工具分析js


image.png

可以看出品牌數(shù)據(jù)來源于接口 http://api.car.bitauto.com/CarInfo/getlefttreejson.ashx?tagtype=chexing&pagetype=masterbrand&objid=0,解析該接口返回的數(shù)據(jù)就可以拿到所有的品牌數(shù)據(jù). 但是有點小坑的是返回的數(shù)據(jù)不是標準的json,需要截取里面的json部分并為所有key加上雙引號才能正常解析.

車系:
在網(wǎng)站上隨便點開一個品牌,比如http://car.bitauto.com/tree_chexing/mb_9/,通過修改瀏覽器的JavaScript設置然后重載頁面,發(fā)現(xiàn)車系的數(shù)據(jù)沒有受到影響,那么我們就可以正常使用response.xpath()來爬取頁面. 查看網(wǎng)頁的源代碼找到元素位置

image.png

查看不同的品牌發(fā)現(xiàn)有的車系有供應商而有的車系則沒有,那么處理這部分標簽的時候需要區(qū)別對待.

車型:
網(wǎng)頁上進入車型詳情頁可以點擊左側的車型進入,也可以點擊車系詳情中的車型進入詳情頁,所以我們在解析車系的時候就可以順便爬取車型頁了.


image.png

車型的數(shù)據(jù)分為在售車型和停售車型,在售車型可以通過解析頁面的標簽獲取,但是停售車型需要通過接口http://car.bitauto.com/AjaxNew/GetNoSaleSerailListByYear.ashx?csID=2593&year=2017來取得,這個接口返回來的是標準的json可以直接使用.

Spider的全部代碼

# coding=utf-8
import json
import re

import scrapy
from scrapy.spiders import Rule

from scrapy.linkextractors import LinkExtractor
from yiche.items import BrandItem, SerialItem, ModelItem


# json替換key
def replacea(matched):
    return '\"' + matched.group('value') + '\":'


# 解析車系Item
def parse_serial_item(serial, bid, vendor):
    item = SerialItem()
    item['id'] = serial.xpath('div/div/a/@id')[0].re(r'n(\d+)')[0]
    item['bid'] = bid
    item['name'] = serial.xpath('div/div/a/@title')[0].extract()
    item['vendor'] = vendor
    item['logo'] = serial.xpath('div/div/a/img/@src')[0].extract()
    sell = serial.xpath('div/ul/li[@class="price"]/a/text()')[0].re(ur'停售')
    item['sell'] = '0' if sell else '1'
    return item


class YiCheSpider(scrapy.Spider):
    name = "yiche"

    rules = (
        # 所有車系
        Rule(LinkExtractor(allow=(r'http://car\.bitauto\.com/tree_chexing/mb_\d+/$',)), callback='parse_serial', follow=True),
        # 在售車型
        Rule(LinkExtractor(allow=(r'http://car\.bitauto\.com/\w+/$',)), callback='parse_model', follow=True),
        # 停售車型
        Rule(LinkExtractor(allow=(r'http://car\.bitauto\.com/AjaxNew/GetNoSaleSerailListByYear\.ashx?csID=\d+&year=\d+$',)),
             callback='parse_model_selled', follow=True),
    )

    def start_requests(self):
        url = 'http://api.car.bitauto.com/CarInfo/getlefttreejson.ashx?tagtype=chexing&pagetype=masterbrand&objid=0'
        yield scrapy.Request(url, callback=self.parse, dont_filter=True)

    # 解析品牌
    def parse(self, response):
        print '==> %s' % response.url

        result = re.sub('(?P<value>\w+):', replacea, response.text[response.text.find('{'):response.text.rfind('}') + 1])
        data = json.loads(result)
        for char in data['char']:
            print '==> %s' % char
            try:
                for brand in data['brand']['%s' % char]:
                    item = BrandItem()
                    item['id'] = brand['id']
                    item['name'] = brand['name']
                    item['logo'] = 'http://image.bitautoimg.com/bt/car/default/images/logo/masterbrand/png/100/m_%s_100.png' % item['id']
                    item['initial'] = char
                    yield item

                    url = 'http://car.bitauto.com/tree_chexing/mb_%s/' % item['id']
                    request = scrapy.Request(url, callback=self.parse_serial, dont_filter=True)
                    request.meta['bid'] = item['id']
                    yield request
            except KeyError:
                pass

    # 解析車系
    def parse_serial(self, response):
        print '==> %s' % response.url

        bid = response.meta['bid']
        # 品牌下的全部車系及車型
        brands = response.xpath('//*[@id="divCsLevel_0"]/*')
        size = len(brands)
        if size % 2 == 0:
            for i in range(size / 2):
                vendor = brands[i * 2].xpath('a/text()')[0].re(r'(\w+)>>')[0]
                for serial in brands[i * 2 + 1].xpath('div'):
                    item = parse_serial_item(serial, bid, vendor)
                    yield item

                    url = 'http://car.bitauto.com%s' % serial.xpath('div/div/a/@href')[0].extract()
                    request = scrapy.Request(url, callback=self.parse_model, dont_filter=True)
                    request.meta['sid'] = item['id']
                    yield request
        else:
            for serial in brands.xpath('div'):
                item = parse_serial_item(serial, bid, '')
                yield item

                url = 'http://car.bitauto.com%s' % serial.xpath('div/div/a/@href')[0].extract()
                request = scrapy.Request(url, callback=self.parse_model, dont_filter=True)
                request.meta['sid'] = item['id']
                yield request

    # 解析車型
    def parse_model(self, response):
        print '==> %s' % response.url

        sid = response.meta['sid']
        # 在售車型
        classify = ''
        for tr in response.xpath('//*[@id="compare_sale"]/tbody/*'):
            tit = tr.xpath('@class')
            if len(tit) == 0:
                item = ModelItem()
                item['id'] = tr.xpath('td[1]/a[1]/@href')[0].re(r'/m(\d+)/')[0]
                item['sid'] = sid
                item['name'] = tr.xpath('td[1]/a[1]/text()')[0].extract()
                item['classify'] = classify
                item['sell'] = '1'
                yield item
            else:
                ths = tr.xpath('th[1]/text()')
                th = ths[1].extract().rstrip() if len(ths) > 1 else ths[0].extract().rstrip()
                strong = tr.xpath('th[1]/strong/text()')[0].extract()
                classify = strong + (('/' + th) if len(th) > 0 else '')

        # 停售車型
        years = response.xpath('//*[@id="carlist_nosaleyear"]/a/@id').extract()
        for year in years:
            url = 'http://car.bitauto.com/AjaxNew/GetNoSaleSerailListByYear.ashx?csID=%s&year=%s' % (sid, year)
            request = scrapy.Request(url=url, callback=self.parse_model_selled, dont_filter=True)
            request.meta['sid'] = sid
            yield request

    @staticmethod
    def parse_model_selled(response):
        print '==> %s' % response.url

        sid = response.meta['sid']
        try:
            datas = json.loads(response.body_as_unicode())
            for data in datas:
                classify = data['Engine_Exhaust'] + '/' + data['MaxPower'] + ' ' + data['InhaleType']
                for car in data['carList']:
                    item = ModelItem()
                    item['id'] = car['CarID']
                    item['sid'] = sid
                    item['name'] = car['YearType'] + ' ' + car['Name']
                    item['classify'] = classify
                    item['sell'] = '0'
                    yield item
        except ValueError:
            print 'model parse error,serial_id[%s].' % sid
            pass

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末歇竟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辫继,老刑警劉巖键耕,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咧叭,死亡現(xiàn)場離奇詭異葱跋,居然都是意外死亡澳化,警方通過查閱死者的電腦和手機崔步,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缎谷,“玉大人井濒,你說我怎么就攤上這事∩髁辏” “怎么了眼虱?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長席纽。 經(jīng)常有香客問我捏悬,道長,這世上最難降的妖魔是什么润梯? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任过牙,我火速辦了婚禮,結果婚禮上纺铭,老公的妹妹穿的比我還像新娘寇钉。我一直安慰自己,他們只是感情好舶赔,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布扫倡。 她就那樣靜靜地躺著,像睡著了一般竟纳。 火紅的嫁衣襯著肌膚如雪撵溃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天锥累,我揣著相機與錄音缘挑,去河邊找鬼。 笑死桶略,一個胖子當著我的面吹牛语淘,可吹牛的內容都是我干的诲宇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惶翻,長吁一口氣:“原來是場噩夢啊……” “哼姑蓝!你這毒婦竟也來了?” 一聲冷哼從身側響起维贺,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤它掂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后溯泣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡榕茧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年垃沦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片用押。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡肢簿,死狀恐怖,靈堂內的尸體忽然破棺而出蜻拨,到底是詐尸還是另有隱情池充,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布缎讼,位于F島的核電站收夸,受9級特大地震影響,放射性物質發(fā)生泄漏血崭。R本人自食惡果不足惜卧惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望夹纫。 院中可真熱鬧咽瓷,春花似錦、人聲如沸舰讹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽月匣。三九已至钻洒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間桶错,已是汗流浹背航唆。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留院刁,地道東北人糯钙。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親任岸。 傳聞我的和親對象是個殘疾皇子再榄,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,162評論 25 707
  • 最近想在工作相關的項目上做技術改進,需要全而準的車型數(shù)據(jù)享潜,尋尋覓覓而不得困鸥,所以就只能自己動手豐衣足食,到網(wǎng)上獲(竊...
    littlelory閱讀 3,900評論 7 19
  • 昨天大概睡了兩個小時不到剑按。 半夜12點多就醒來了疾就,睡不著又很餓,結果一開始吃就停不下來了艺蝴,把冰箱里僅剩下的存糧都吃...
    臉大的胖妞閱讀 181評論 0 0
  • 兩只眼皮在打架猬腰,我的月亮去哪啦? 月亮你真不該呀猜敢,誰把思念寄予他姑荷? 沒能抓住佳節(jié)的尾巴,卻無法忘記他缩擂! 明明放不下...
    瞿靜閱讀 197評論 0 0
  • 他把自己的創(chuàng)業(yè)經(jīng)驗總結成為「3+1理論」鼠冕,「第一要做剛需;第二是項目能完成小型閉環(huán)胯盯;第三是要有現(xiàn)金流懈费。再加一點『是...
    小飛機1948閱讀 146評論 0 0