1. 程式人生 > >【汽車口碑分析】3.爬取汽車評論資料

【汽車口碑分析】3.爬取汽車評論資料

環境配置

  • Ubuntu 16.04
  • Python 3.5

技術框架

  • Scrapy

需求目標

本專案為汽車口碑分析,第一步需要爬取對於不同車型的評論資料。

選擇58車的車型分類爬取評論資料。

爬取流程

  1. 先獲取每個車型的連結,以下圖中紅框內的車型為例

    mark

  2. 開啟連結後,抓取下圖紅框中的總評分,寫入檔案中。

    mark

  3. 寫入總評分後,通過拼接連結進入該車型的使用者評論頁面。

    通過第一步中獲取的連結拼接上list_s1_p1.html,組成使用者評論頁面的連結。

    【注】此為第一頁的連結,若還有下一頁,下述步驟會提及處理方法。

    mark

  4. 抓取評論頁面中的各種資料,如id評分

    評論等。

    mark

  5. 若該評論頁面還有下一頁,則繼續抓取下一頁中的評論資料。

    【方法】

    判斷頁面中是否有下一頁元素,若有則回撥解析評論頁面的方法。

  6. 將爬取的資料儲存到檔案中。

詳細步驟

建立新工程

先建立工程目錄

cd /home/t/dataset/
mkdir carSpider

建立新工程

scrapy startproject carSpider

編輯items.py檔案

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html import scrapy class CarspiderItem(scrapy.Item): file=scrapy.Field() #檔名 car=scrapy.Field() #車型 score=scrapy.Field() #總評分 u_id=scrapy.Field() #使用者ID u_score=scrapy.Field() #使用者評分 u_merit=scrapy.Field() #使用者評論優點 u_demerit=scrapy.Field() #使用者評論缺點
u_summary=scrapy.Field() #使用者評論綜述 u_flower=scrapy.Field() #使用者評論鮮花數 u_brick=scrapy.Field() #使用者評論板磚數

編寫carSpider.py檔案

import scrapy
from carSpider.items import CarspiderItem

baseDir = '/home/t/dataset/carRemark/'
startUrl='http://www.58che.com/brand.html'

class CarSpider(scrapy.Spider):

    name='spider' #爬蟲名
    def __init__(self):
        self.start_urls=[startUrl] 

    #第一層解析方法
    def parse(self,response):
        #定位到車型元素
        subclasses=response.css('body > div.fltop > div.marcenter > div > div > div.r > ul > li > dl > dt > a')
        for subclass in subclasses:
            subclass_name=subclass.xpath('text()').extract_first() #獲取車型名稱文字
            subclass_link=subclass.xpath('@href').extract_first() #獲取車型連結
            yield scrapy.Request(url=subclass_link,callback=self.parse_car_subclass,meta={'file':subclass_name}) #回撥下一層解析方法,並把車型名稱傳遞給該方法作為檔名

    #第二層解析方法
    def parse_car_subclass(self,response):
        infos=response.css('#line1 > div.cars_line2.l > div.dianpings > div.d_div1.clearfix > font') #定位到總評分元素
        for info in infos:
            score=info.xpath('text()').extract_first() #獲取總評分元素文字
            file=response.meta['file'] #獲取上個Request傳遞來的meta['file']
            self.writeScore(file,score) #將總評分寫入檔案中
            link=response.url+'list_s1_p1.html' #拼接使用者評論第一頁連結
            yield scrapy.Request(url=link,callback=self.parse_remark,meta={'file':file}) #回撥下一層解析方法,把車型名稱傳遞給該方法作為檔名

    #第三層解析方法
    def parse_remark(self,response):
        #定位到使用者評論元素
        infos=response.css('body > div.newbox > div > div.xgo_cars_w760.l > div.xgo_dianping_infos.mb10 > div.xgo_cars_dianping > div > dl')
        for info in infos:
            uid=info.xpath('dd[1]/strong/a/text()')[0].extract() #獲取使用者ID
            score=info.xpath('dd[1]/div/div/@style')[0].extract() #獲取使用者評分星級
            score=self.getScore(score) #將使用者評分星級轉化為5分制評分

            try:
                #先獲取是否有‘優點’元素,若有則定位‘優點’元素的下一個兄弟節點,即‘優點評語’,若無則為空
                node=info.xpath('dd[2]/div/div[contains(@class,"l redc00")]')[0] 
                if node is not None:
                    merit=node.xpath('following-sibling::*[1]/text()')[0].extract()
                else:
                    merit=''
            except:
                merit=''


            try:
                #先獲取是否有‘缺點’元素,若有則定位‘缺點’元素的下一個兄弟節點,即‘缺點評語’,若無則為空
                node=info.xpath('dd[2]/div/div[contains(@class,"l hei666")]')[0]
                if node is not None:
                    demerit=node.xpath('following-sibling::*[1]/text()')[0].extract()
                else:
                    demerit=''
            except:
                demerit=''

            try:
                #先獲取是否有‘綜述’元素,若有則定位‘綜述’元素的下一個兄弟節點,即‘綜述評語’,若無則為空
                node=info.xpath('dd[2]/div/div[contains(@class,"l")]')[0]
                if node is not None:
                    summary=node.xpath('following-sibling::*[1]/text()')[0].extract()
                else:
                    summary=''
            except:
                summary=''

            flower=info.xpath('dd[2]/div[contains(@class,"apply")]/a[3]/span/text()')[0].extract() #獲取鮮花數
            brick=info.xpath('dd[2]/div[contains(@class,"apply")]/a[4]/span/text()')[0].extract() #獲取板磚數

           #建立Item
            item=CarspiderItem()
            item['file']=response.meta['file']
            item['u_id']=uid
            item['u_score']=score
            item['u_merit']=merit
            item['u_demerit']=demerit
            item['u_summary']=summary
            item['u_flower']=flower
            item['u_brick']=brick

            #生成Item
            yield item

        #獲取`下一頁`元素,若有則回撥`parse_remark`第三層解析方法,即繼續獲取下一頁使用者評論資料
        #定位`下一頁`元素
        next_pages=response.css('body > div.newbox > div > div.xgo_cars_w760.l > div.xgo_dianping_infos.mb10 > div.xgo_cars_dianping > div > div > a.next')
        for next_page in next_pages:
            #若有`下一頁`元素,則拼接`下一頁`元素連結,並回調第三層解析方法,用來獲取下一頁使用者評論資料
            if next_page is not None:
                next_page_link=next_page.xpath('@href')[0].extract()
                next_page_link='http://www.58che.com'+next_page_link
                file=response.meta['file']
                yield scrapy.Request(url=next_page_link, callback=self.parse_remark, meta={'file': file})


    #將總評分寫入檔案
    def writeScore(self,file,score):
        with open('/home/t/dataset/carRemark/'+file+'.json','a+') as f:
            f.write(score+'\n')

    #將使用者評分星級轉為5分制分數,類似switch功能
    def getScore(self,text):
        text=text.split(':')[1] #分割文字,原文字格式形如`width:100%`,分割並擷取`:`後的文字
        return {
            '100%':5,
            '80%':4,
            '60%':3,
            '40%':2,
            '20%':1,
            '0%':0
        }.get(text)

【解析】

        #定位到使用者評論元素
        infos=response.css('body > div.newbox > div > div.xgo_cars_w760.l > div.xgo_dianping_infos.mb10 > div.xgo_cars_dianping > div > dl')

此句程式碼定位的元素如下圖所示,定位到的是評論頁面每條評論的元素整體。

mark

 for info in infos:
            uid=info.xpath('dd[1]/strong/a/text()')[0].extract() #獲取使用者ID
            score=info.xpath('dd[1]/div/div/@style')[0].extract() #獲取使用者評分星級
            score=self.getScore(score) #將使用者評分星級轉化為5分制評分

uid定位到的元素如下圖所示,

mark

score定位到的元素如下圖所示,獲取score元素的style屬性,值形如width:80%,需要通過getScore()方法轉換為五分制分數。

mark

try:
    #先獲取是否有‘優點’元素,若有則定位‘優點’元素的下一個兄弟節點,即‘優點評語’,若無則為空
    node=info.xpath('dd[2]/div/div[contains(@class,"l redc00")]')[0] 
    if node is not None:
        merit=node.xpath('following-sibling::*[1]/text()')[0].extract()
    else:
        merit=''
except:
    merit=''

先定位是否有優點元素,如下圖紅框所示,若有該元素,則獲取優點元素的下一個兄弟節點內容,如下圖藍框所示,若無則為空。

mark

#獲取`下一頁`元素,若有則回撥`parse_remark`第三層解析方法,即繼續獲取下一頁使用者評論資料
#定位`下一頁`元素
next_pages=response.css('body > div.newbox > div > div.xgo_cars_w760.l > div.xgo_dianping_infos.mb10 > div.xgo_cars_dianping > div > div > a.next')
    for next_page in next_pages:
        #若有`下一頁`元素,則拼接`下一頁`元素連結,並回調第三層解析方法,用來獲取下一頁使用者評論資料
        if next_page is not None:
            next_page_link=next_page.xpath('@href')[0].extract()
             next_page_link='http://www.58che.com'+next_page_link
             file=response.meta['file']
             yield scrapy.Request(url=next_page_link, callback=self.parse_remark, meta={'file': file})

解析完上述內容,判斷使用者評論頁面是否有分頁,定位是否有下一頁元素,如下圖紅框所示,若有則獲取該元素連結,如下圖橙框所示。

獲取之後,回撥parse_remark方法解析下一頁的評論頁面。

mark

編輯pipelines.py檔案

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html

import json
import codecs

baseDir = '/home/t/dataset/carRemark/'
class CarspiderPipeline(object):
    def process_item(self, item, spider):
        print(item['file'])
        with codecs.open(baseDir+item['file']+'.json','a+',encoding='utf-8') as f:
            line=json.dumps(dict(item),ensure_ascii=False)+'\n'
            f.write(line)

        return item

編輯settings.py檔案

# -*- coding: utf-8 -*-

# Scrapy settings for carSpider project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     http://doc.scrapy.org/en/latest/topics/settings.html
#     http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
#     http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html

BOT_NAME = 'carSpider'

SPIDER_MODULES = ['carSpider.spiders']
NEWSPIDER_MODULE = 'carSpider.spiders'


# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'carSpider (+http://www.yourdomain.com)'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)
#COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False

# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}

# Enable or disable spider middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    'carSpider.middlewares.CarspiderSpiderMiddleware': 543,
#}

# Enable or disable downloader middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
#    'carSpider.middlewares.MyCustomDownloaderMiddleware': 543,
#}

# Enable or disable extensions
# See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    'scrapy.extensions.telnet.TelnetConsole': None,
#}

# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'carSpider.pipelines.CarspiderPipeline': 300,
}

# Enable and configure the AutoThrottle extension (disabled by default)
# See http://doc.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False

# Enable and configure HTTP caching (disabled by default)
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
HTTPCACHE_ENABLED = False
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

【解析】

ROBOTSTXT_OBEY = False

將原來的True改為False

ITEM_PIPELINES = {
    'carSpider.pipelines.CarspiderPipeline': 300,
}

將原來的註釋去掉,即註冊pipelines,否則無法使用該pipelines。

執行爬蟲

在專案根目錄下新建檔案entrypoint.py

mark

from scrapy.cmdline import execute
execute(['scrapy','crawl','spider'])

專案原始碼