Scrapy分布式爬蟲---爬取伯樂在線所有文章

---爬取伯樂在線所有文章---

1,scrapy安裝及目錄結(jié)構(gòu)介紹

  • 電腦的基礎配置撩轰,需要的開發(fā)工具
1.python 3.5.3
2.PyCharm 2016.3
3.mysql+navicat for mysql
  • 基礎虛擬環(huán)境的搭建和配置
pip install virtualenv
pip install virtualenvwrapper-win
安裝虛擬環(huán)境管理
mkvirtualenv article_spider
創(chuàng)建虛擬環(huán)境
workon articles_spider
直接進入虛擬環(huán)境
deactivate
退出激活狀態(tài)
workon
知道有哪些虛擬環(huán)境
  • 命令行創(chuàng)建scrapy項目
pip install -i https://pypi.douban.com/simple/ scrapy
在虛擬環(huán)境下使用豆瓣源安裝scrapy
scrapy startproject articlespider
創(chuàng)建scrapy項目
  • scrapy目錄結(jié)構(gòu)

scrapy.cfg

scrapy.cfg:配置文件.   scrapy借鑒了django的項目思想

settings.py:關于scrapy的配置

SPIDER_MODULES = ['articlespider.spiders']#存放spiders的路徑
NEWSPIDER_MODULE = 'articlespider.spiders'

pipelines.py:做跟數(shù)據(jù)存儲相關的文件
middlewares.py:自己自定義middlewares,讓scrapy變得更加可控
items.py:定義數(shù)據(jù)保存的格式胯甩,保存我們所爬取到的數(shù)據(jù)

  • 創(chuàng)建我們的spider: jobbole.py
workon article_spider
scrapy genspider jobbole blog.jobbole.com

可以看到直接為我們創(chuàng)建好的空項目里已經(jīng)有了模板代碼。如下:

# -*- coding: utf-8 -*-
import scrapy
class JobboleSpider(scrapy.Spider):#繼承scrapy.Spider
    name = 'jobbole'
    allowed_domains = ['blog.jobbole.com']#域名
    start_urls = ['http://blog.jobbole.com/']#爬蟲爬取的起始url

    def parse(self, response):#parse函數(shù)里面的邏輯是我們需要寫的東西
          pass
  • scrapy執(zhí)行流程

在命令行中啟動剛剛創(chuàng)建的spider:

(article_spider) D:\LinuxShare\articlespider>scrapy crawl jobbole

在windows報出錯誤:

ImportError: No module named 'win32api'
(article_spider) D:\LinuxShare\articlespider>pip install -i https://pypi.douban.com/simple pypiwin32
#安裝pypiwin32解決

創(chuàng)建我們的調(diào)試工具類:在項目根目錄里創(chuàng)建main.py作為調(diào)試工具文件

# _*_ coding: utf-8 _*_
from scrapy.cmdline import execute

import sys
import os

#將系統(tǒng)當前目錄設置為項目根目錄
os.path.abspath(__file__)#為當前文件所在絕對路徑-articlespider
os.path.dirname#為文件所在目錄-D:\LinuxShare\

sys.path.append(os.path.dirname(os.path.abspath(__file__)))
#執(zhí)行命令堪嫂,相當于在控制臺cmd輸入改名了
execute(["scrapy", "crawl" , "jobbole"])#執(zhí)行命令scrapy crawl jobbole

settings.py里面的設置要不遵循reboots協(xié)議偎箫,防止scrapy過濾掉不符合reboots協(xié)議的url

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

在jobble.py打上斷點:

def parse(self, response):
      pass#打斷點處

可以看到返回的html的response對象:


response.PNG

對象內(nèi)部中的body是整個網(wǎng)頁源文件的內(nèi)容,而且scrapy幫我們做了編碼轉(zhuǎn)換為utf-8格式皆串。

DEFAULT_ENCODING={str}'ascii'
encoding={str}'utf-8'

2,提取伯樂在線內(nèi)容

  • xpath的使用

為什么要使用xpath?
1.xpath使用路徑表達式在xml和html中進行導航
2.xpath包含一個標準函數(shù)庫
3.xpath是一個w3c標準
4.xpath速度快于beautifulsoup

xpath節(jié)點的關系:
1.父節(jié)點 2.子節(jié)點 3.同胞節(jié)點 4.先輩節(jié)點 5.后代節(jié)點

xpath語法:


xpath語法(一).PNG

xpath語法--謂語.PNG

xpath語法(二).PNG

獲取一個網(wǎng)頁上元素的xpath地址如:http://blog.jobbole.com/113722/

        re_selector=response.xpath('//*[@id="post-113722"]/div[1]/h1/text()')
        #獲取文章的標題
        re_selector1=response.xpath('//div[@class="entry-header"]/h1/text()')

id型是比較精準的定位淹办,可以在Chrome源代碼中使用copy xpath直接復制
推薦使用class型,比較方便恶复,不過在書寫class型xpath是要查詢源代碼中是否有重復的class屬性值

在shell腳本中進行調(diào)試

(article_spider) D:\LinuxShare\articlespider>scrapy shell http://blog.jobbole.com/113722/

完整的xpath提取文章具體字段的代碼:

# -*- coding: utf-8 -*-
import scrapy
import re

class JobboleSpider(scrapy.Spider):
    name = 'jobbole'
    allowed_domains = ['blog.jobbole.com']
    start_urls = ['http://blog.jobbole.com/113722/']

    def parse(self, response):
        #提取文章的具體字段
     
        re_selector=response.xpath('//*[@id="post-113722"]/div[1]/h1/text()')
        re_selector1=response.xpath('//div[@class="entry-header"]/h1/text()')
        title=response.xpath('//div[@class="entry-header"]/h1/text()').extract_first("")
        create_date= response.xpath("http://p[@class='entry-meta-hide-on-mobile']/text()").extract()[0].strip().replace("·","").strip()
        praise_nums=response.xpath("http://span[contains(@class,'vote-post-up')]/h10/text()").extract()[0]

        fav_nums=response.xpath("http://span[contains(@class,'bookmark-btn')]/text()").extract()[0]
        match_re=re.match(".*?(\d+).*",fav_nums)#正則表達式匹配
        if match_re:
            fav_nums=int(match_re.group(1))
        else:
            fav_nums=0

        comments_nums = response.xpath("http://a[@href='#article-comment']/span/text()").extract()[0]
        match_re = re.match(".*?(\d+).*", comments_nums)
        if match_re:
            comments_nums = int(match_re.group(1))
        else:
            comments_nums=0

        content=response.xpath("http://div[@class='entry']").extract()[0]#取全文內(nèi)容
        create_date = response.xpath("http://p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
        tag_list=response.xpath("http://p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
        tag_list=[element for element in tag_list if not element.strip().endswith("評論")]  # 去重“評論”
        tags=",".join(tag_list)
  • css選擇器的使用

css選擇器用于描述一組元素的樣式怜森,這里不做過多的介紹,通過css選擇器提取文章字段的代碼如下所示:

# -*- coding: utf-8 -*-
import scrapy
import re

class JobboleSpider(scrapy.Spider):
    name = 'jobbole'
    allowed_domains = ['blog.jobbole.com']
    start_urls = ['http://blog.jobbole.com/113722/']

    def parse(self, response):
#通過css選擇器提取字段
        front_image_url=response.meta.get("front_image_url","")#文章封面圖
        title=response.css(".entry-header h1::text").extract()[0]#::text  偽類選擇器
        create_date=response.css("p.entry-meta-hide-on-mobile::text").extract()[0].strip().replace("·","").strip()
        praise_nums=response.css(".vote-post-up h10::text").extract()[0]

        fav_nums=response.css("span.bookmark-btn::text").extract()[0]
        match_re = re.match(".*?(\d+).*", fav_nums)
        if match_re:
            fav_nums = int(match_re.group(1))
        else:
            fav_nums=0
        comments_nums= response.css("a[href='#article-comment'] span::text").extract()[0]
        match_re = re.match(".*?(\d+).*", comments_nums)
        if match_re:
            comments_nums = int(match_re.group(1))
        else:
            comments_nums=0
        content=response.css("div.entry").extract()[0]
        tag_list=response.css("p.entry-meta-hide-on-mobile a::text").extract()
        tag_list = [element for element in tag_list if not element.strip().endswith("評論")]  # 去重“評論”
        tags = ",".join(tag_list)

3,編寫spider爬取jobbole的所有文章

  • 解析列表頁中所有文章的url并交給scrapy下載進行解析
start_urls = ['http://blog.jobbole.com/all-posts/']
...
post_urls = response.css("#archive .floated-thumb .post-thumb a::attr(href)").extract()
  • Request下載網(wǎng)頁
from scrapy.http import Request#把Request里面的url交給scrapy
...
Request(url=post_url,callback=self.parse_detail)
  • parse函數(shù)完成url拼接防止出現(xiàn)href元素內(nèi)網(wǎng)址不全的情況
from urllib import parse#url可能出現(xiàn)無域名的情況寂玲,這時候引用函數(shù)parse
...
Request(url=parse.urljoin(response.url,post_url),callback=self.parse_detail)
  • yield關鍵字
#使用request下載詳情頁面塔插,下載完成后回調(diào)方法parse_detail()提取文章內(nèi)容中的字段
yield Request(url=parse.urljoin(response.url,post_url),callback=self.parse_detail)
#yield關鍵字用于把Request提取到的內(nèi)容交給scrapy進行下載
  • 提取“下一頁”的url
next_urls=response.css(".next.page-numbers::attr(href)").extract()[0]#next 和 page-numbers是兩個class,這樣表示這個節(jié)點有next和page
        if next_urls:
            yield Request(url=parse.urljoin(response.url,next_urls),callback=self.parse)
#注意callback=self.parse這里傳入parse函數(shù)并沒有進行調(diào)用
  • 實現(xiàn)全部文章字段遍歷和解析的代碼如下:
def parse(self, response):
        """
                1. 獲取文章列表頁中的文章url并交給scrapy下載后并進行解析
                2. 獲取下一頁的url并交給scrapy進行下載, 下載完成后交給parse
                """
        # 解析列表頁中的所有文章url并交給scrapy下載后并進行解析
        post_urls = response.css("#archive .floated-thumb .post-thumb a::attr(href)").extract()
        for post_url in post_urls:
            #request下載完成之后拓哟,回調(diào)parse_detail進行文章詳情頁的解析
            # Request(url=post_url,callback=self.parse_detail)
            print(response.url)
            print(post_url)
            yield Request(url=parse.urljoin(response.url,post_url),callback=self.parse_detail)
            #遇到href沒有域名的解決方案
            #response.url + post_url
            print(post_url)
        # 提取下一頁并交給scrapy進行下載
        next_url = response.css(".next.page-numbers::attr(href)").extract_first("")
        if next_url:
            yield Request(url=parse.urljoin(response.url, post_url), callback=self.parse)
  • 全部文章字段解析和下載的邏輯流程圖:
全部文章的邏輯流程圖.png

4,scrapy的items整合字段

  • 數(shù)據(jù)爬取的任務就是從非結(jié)構(gòu)的數(shù)據(jù)中提取出結(jié)構(gòu)性的數(shù)據(jù)伶授。
    items 可以讓我們自定義返回自己所提取的字段(類似于字典断序,但比字典的功能更齊全),我們可以將實例化的items直接yield糜烹,這樣scrapy就可以幫我們把items路由到pipelines中违诗,方便這些字段數(shù)據(jù)集中在pipelines中的保存,去重等等

  • 獲取列表頁封面圖的url,并通過Request傳到response里面疮蹦,在文章詳情頁中可能沒有封面圖的url诸迟,所以要在下載網(wǎng)頁時把這個url獲取到,這里要用到Request里面的meta{}愕乎,meta在這里表示一個字典的樣式阵苇。

yield Request(url=parse.urljoin(response.url,post_url),meta={"front_image_url":image_url},callback=self.parse_detail)

front_image_url = response.meta.get("front_image_url", "")#或取文章封面圖
  • url嵌套的方法
post_nodes=response.css("#archive .floated-thumb .post-thumb a")#提取文章列表的url后交給scrapy進行下載并進行解析
        for post_node in post_nodes:
            image_url=post_node.css("img::attr(src)").extract_first("")#post_nodes下的url中進行進一步的篩選
            post_url=post_node.css("::attr(href)").extract_first("")#同image_url
            yield Request(url=parse.urljoin(response.url, post_url), meta={"front_image_url": parse.urljoin(response.url, image_url)},
                              callback=self.parse_detail)
  • urljoin的好處:如果<img src="">里面沒有域名,那就取response.url感论,如果有域名就取post.url


    urljoin.PNG
  • 在items.py中編寫我們自定義的item:

class JobBolearticleItem(scrapy.Item):
    title = scrapy.Field()
    create_date = scrapy.Field()
    url = scrapy.Field()
    url_object_id = scrapy.Field()
    front_image_url = scrapy.Field()
    front_image_path = scrapy.Field()
    praise_nums = scrapy.Field()
    comments_nums = scrapy.Field()
    fav_nums = scrapy.Field()
    content = scrapy.Field()
    tags = scrapy.Field()
  • import之后實例化绅项,實例化之后填充:
     1. from ArticleSpider.items import JobBolearticleItem
     2. article_item = JobBolearticleItem()
     3. article_item["title"] = title
        article_item["url"] = response.url
        article_item["create_date"] = create_date
        article_item["front_image_url"] = [front_image_url]
        article_item["praise_nums"] = praise_nums
        article_item["comments_nums"] = comments_nums
        article_item["fav_nums"] = fav_nums
        article_item["tags"] = tags
        article_item["content"] = content

        yield article_item#將填充進來的item傳送到pipelines中

  • 下載圖片:scrapy提供了自動下載圖片的機制,在settings.py里面設置好要下載圖片的pipeline比肄,數(shù)字越小表示越優(yōu)先進入快耿。
ITEM_PIPELINES={
'articlespider.pipelines.articleImagePipeline':300,
'scrapy.pipelines.images.ImagesPipeline':1,
}
  • 新建文件夾images囊陡,圖片保存的相對路徑
IMAGES_URLS_FIELD = "front_image_url"#下載圖片的地址
project_dir = os.path.abspath(os.path.dirname(__file__))#通過os獲取當前文件所在的相對路徑
IMAGES_STORE = os.path.join(project_dir, 'images')#存放圖片的目錄,圖片的保存路徑
  • 異常處理:ValueError和ImportError 解決辦法:front_image_url設置成數(shù)組格式掀亥,安裝pillow庫撞反。

  • 自定義articleImagePipeline用來存放獲取的封面圖

from scrapy.pipelines.images import ImagesPipeline
...
class articleImagePipeline(ImagesPipeline):#繼承ImagesPipeline,只用來處理封面圖的自定義的pipeline搪花,
    def item_completed(self,results,item,info):
        pass
  • 在函數(shù)item_completed下的result下的字典dict下獲取到圖片保存的地址path


    path路徑.PNG
  • 繼承ImagesPipeline并重寫item_completed痢畜,獲取到path

from scrapy.pipelines.images import ImagesPipeline

class articleImagePipeline(ImagesPipeline):
    #重寫該方法可從result中獲取到圖片的實際下載地址
    def item_completed(self, results, item, info):
        for ok, value in results:
            image_file_path = value["path"]
        item["front_image_path"] = image_file_path

        return item
  • setting.py也要使用我們所自定義的pipeline,而不是用模塊自帶的
ITEM_PIPELINES = {
   'articlespider.pipelines.ArticlespiderPipeline':300,
   #'scrapy.pipelines.images.ImagesPipeline':1,
   'articlespider.pipelines.articleImagePipeline':1,
}
  • 圖片url的md5處理,,新建一個文件夾utils:
import hashlib#md5里面的url需要引進hashlib函數(shù)


#定義一個md5函數(shù)
def get_md5(url):
    if isinstance(url,str):
        url=url.encode("utf-8")#判斷url是否為unicode鳍侣,如果是丁稀,轉(zhuǎn)成encode
    m=hashlib.md5()
    m.update(url)
    return m.hexdigest()

if __name__=="__main__":
    print (get_md5("http://jobbole.com".encode("utf-8")))#python3里面的字符都是unicode格式的,而hashlib不支持unicode倚聚,所以要轉(zhuǎn)成encode
  • 在jobbole.py中將md5保存下來
from articlespider.utils.common import get_md5
...
article_item["url_object_id"]=get_md5(response.url)

5线衫,數(shù)據(jù)保存到本地文件及mysql中

  • 保存item到json文件中
    import codecs使打開文件時避免了一些編碼問題,自定義JsonWithEncodingPipeline實現(xiàn)json文件本地保存
import codecs
...
class JsonWithEncodingPipeline(object):#繼承object
    def __init__(self):#初始化的時候打開json文件
        self.file=codecs.open('article.json','w',encoding="utf-8")#通過codecs方法寫入article.json

    def process_item(self, item, spider):
        lines=json.dumps(dict(item),ensure_ascii=False) + "\n"#把item寫入json當中惑折,item要轉(zhuǎn)換成dict才能dumps
        self.file.write(lines)#寫入article.json當中
        return item

    def spider_closed(self,spider):#當出現(xiàn)spider_closed,關閉這個文件的寫入
        self.file.close()

settings.py中注冊JsonWithEncodingPipeline:

ITEM_PIPELINES = {
   'articlespider.pipelines.JsonWithEncodingPipeline':2,
   #'scrapy.pipelines.images.ImagesPipeline':1,
   'articlespider.pipelines.articleImagePipeline':1,
}

debug main.py文件發(fā)現(xiàn)生成了article.json文件

  • 調(diào)用scrapy提供的json export導出json文件
from scrapy.exporters import JsonItemExporter
...
class JsonExporterPipleline(object):
    #調(diào)用scrapy提供的json export導出json文件
    def __init__(self):
        self.file = open('articleexport.json', 'wb')
        self.exporter = JsonItemExporter(self.file, encoding="utf-8", ensure_ascii=False)
        self.exporter.start_exporting()


    def close_spider(self, spider):
        self.exporter.finish_exporting()
        self.file.close()


    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item

在settings.py中注冊JsonExporterPipleline:

ITEM_PIPELINES = {
   'articlespider.pipelines.JsonExporterPipleline':2,
   #'scrapy.pipelines.images.ImagesPipeline':1,
   'articlespider.pipelines.articleImagePipeline':1,
}

debug main.py文件發(fā)現(xiàn)生成了articleexport.json文件

  • 數(shù)據(jù)表設計
數(shù)據(jù)表.PNG

注意:三個num字段不能設置為空授账,默認值為零;
如果設置url_object_id為主鍵惨驶,則必須在代碼中進行插入操作白热。

  • 通過pipeline保存數(shù)據(jù)到mysql

數(shù)據(jù)庫驅(qū)動安裝

pip install -i https://pypi.douban.com/simple/ mysqlclient

pipelines采用同步機制寫入mysql

import MySQLdb
...
class MysqlPipeline(object):
    #采用同步的機制寫入mysql
    def __init__(self):
        self.conn = MySQLdb.connect('127.0.0.1', 'root', 'root', 'articlespider', charset="utf8", use_unicode=True)
        self.cursor = self.conn.cursor()

    def process_item(self, item, spider):
        insert_sql = """
            insert into jobbole_article(title, url, create_date, fav_nums,url_object_id)
            VALUES (%s, %s, %s, %s,%s)
        """
        self.cursor.execute(insert_sql, (item["title"], item["url"], item["create_date"], item["fav_nums"],item["url_object_id"]))
        self.conn.commit()

pipelines采用異步機制寫入mysql

因為我們爬取網(wǎng)頁url的速度后期可能會大于數(shù)據(jù)庫存儲的速度,采用twisted框架可以讓我們數(shù)據(jù)庫的插入變成一種異步的操作粗卜。

在settings.py中設置可配置參數(shù):

MYSQL_HOST = "127.0.0.1"
MYSQL_DBNAME = "articlespider"
MYSQL_USER = "root"
MYSQL_PASSWORD = "root"

pipelines異步機制代碼:

from twisted.enterprise import adbapi#adbapi可以將mysqldb里面的一些操作變成異步化的操作
import MySQLdb.cursors
...
class MysqlTwistedPipeline(object):
    def __init__(self, dbpool):#接收dbpool
        self.dbpool = dbpool

    @classmethod
    def from_settings(cls,settings):#定義from_settings直接取settings中定義的值
        dbparms = dict(#傳入的參數(shù)要和MySQLdb.connect里面的connection下面的參數(shù)一致
            host=settings["MYSQL_HOST"],
            db=settings["MYSQL_DBNAME"],
            user=settings["MYSQL_USER"],
            passwd=settings["MYSQL_PASSWORD"],
            charset='utf8',
            cursorclass=MySQLdb.cursors.DictCursor,
            use_unicode=True,
        )
        dbpool = adbapi.ConnectionPool("MySQLdb", **dbparms)#這是一個連接池屋确,MYSQLdb是adbapi里面的dbapiName,**dbparms是要傳入的參數(shù)
        return cls(dbpool)#返回一個實例

    def process_item(self, item, spider):
        # 使用twisted將mysql插入變成異步執(zhí)行
        query = self.dbpool.runInteraction(self.do_insert, item)
        query.addErrback(self.handle_error, item, spider)  # 處理異常


    def handle_error(self, failure, item, spider):
        # 處理異步插入的異常
        print(failure)


    def do_insert(self, cursor,item):
        insert_sql = """
            insert into jobbole_article(title, url, create_date, fav_nums,url_object_id)
            VALUES (%s, %s, %s, %s,%s)
        """
        cursor.execute(insert_sql,
                            (item["title"], item["url"], item["create_date"], item["fav_nums"], item["url_object_id"]))
  • scrapy提供的itemloader來提取字段

itemloader提供了一個容器续扔,讓我們配置提取字段的規(guī)則:
add_xpath,add_css,add_value

from scrapy.loader import ItemLoader
...
#通過Item Loader加載item攻臀,scrapy提供的提取字段的配置函數(shù)
        front_image_url = response.meta.get("front_image_url", "")  # 文章封面圖
        item_loader=ArticleItemLoader(item=JobBolearticleItem(),response=response)#這里的ItemLoader要換成自定義的ArticleItemLoader
        item_loader.add_css("title", ".entry-header h1::text")
        item_loader.add_value("url", response.url)
        item_loader.add_value("url_object_id", get_md5(response.url))
        item_loader.add_css("create_date", "p.entry-meta-hide-on-mobile::text")
        item_loader.add_value("front_image_url", [front_image_url])
        item_loader.add_css("praise_nums", ".vote-post-up h10::text")
        item_loader.add_css("comments_nums", "a[href='#article-comment'] span::text")
        item_loader.add_css("fav_nums", ".bookmark-btn::text")
        item_loader.add_css("tags", "p.entry-meta-hide-on-mobile a::text")
        item_loader.add_css("content", "div.entry")

        article_item = item_loader.load_item()

        yield article_item#將填充進來的item傳送到pipelines中
itemloader.PNG

會發(fā)現(xiàn)所有值變成了list,因此需要處理函數(shù)來對這些值進行過濾纱昧。

MapCompose可以傳入函數(shù)對于該字段進行處理刨啸,而且可以傳入多個:

from scrapy.loader.processors import MapCompose
def add_mtianyan(value):
    return value+"-mtianyan"

 title = scrapy.Field(
        input_processor=MapCompose(lambda x:x+"mtianyan",add_mtianyan),
    )

自定義ArticleItemLoader來取list里面的第一個值,類似于正則表達式里面的extract_first(""):

from scrapy.loader import ItemLoader#自定義的ArticleItemLoader需要繼承ItemLoader
...
class ArticleItemLoader(ItemLoader):
    #自定義itemloader
    default_output_processor = TakeFirst()#此函數(shù)用來取數(shù)組中的第一個數(shù)據(jù)识脆,但是是str類型

保存[front_image_url]的list值:

def return_value(value):#front_image_url調(diào)用
    return value
...
class JobBolearticleItem(scrapy.Item):
      front_image_url=scrapy.Field(
        output_processor=MapCompose(return_value)#保證front_image_url里面的參數(shù)格式是list,,配置image pipeline之后设联,front_image_url傳入必須是list形式
    )

去掉tags里面的“評論”:

from scrapy.loader.processors import MapCompose, TakeFirst, Join
...
def remove_comment_tags(value):
    #去掉tag中提取的評論
    if "評論" in value:
        return ""
    else:
        return value
...
class ArticleItemLoader(ItemLoader):
      tags=scrapy.Field(
        input_processor=MapCompose(remove_comment_tags),#去掉tags中出現(xiàn)的評論
        output_processor=Join(",")#使用join把字符串連接起來,原理同最開始的寫法
    )

獲取nums:

def get_nums(value):
    match_re = re.match(".*?(\d+).*", value)
    if match_re:
        nums = int(match_re.group(1))
    else:
        nums = 0

    return nums
...
class ArticleItemLoader(ItemLoader):
      praise_nums=scrapy.Field(
          input_processor=MapCompose(get_nums)
      )
      comments_nums=scrapy.Field(
          input_processor=MapCompose(get_nums)
      )
      fav_nums=scrapy.Field(
          input_processor=MapCompose(get_nums)
      )

圖片pipeline里增強if通用性:

class articleImagePipeline(ImagesPipeline):#繼承ImagesPipeline灼捂,只用來處理封面圖的自定義的pipeline离例,


    def item_completed(self,results,item,info):#results里面有兩個參數(shù),list里面有tuple和dict纵东,dict里面的path是image保存的文件的路徑
        if "front_image_url" in item:#知乎等網(wǎng)站可能沒有front_image_url
            for ok, value in results:
                image_file_path = value["path"]
            item["front_image_path"] = image_file_path

        return item

自定義的item帶處理函數(shù)的完整代碼:

class JobBolearticleItem(scrapy.Item):
    title=scrapy.Field()
    create_date=scrapy.Field(
        input_processor=MapCompose(date_convert),
    )
    url=scrapy.Field()
    url_object_id=scrapy.Field()#對url_object_id進行md5處理粘招,讓url變成一個長度固定的,唯一的值
    praise_nums=scrapy.Field(
        input_processor=MapCompose(get_nums)
    )
    comments_nums=scrapy.Field(
        input_processor=MapCompose(get_nums)
    )
    fav_nums=scrapy.Field(
        input_processor=MapCompose(get_nums)
    )
    content=scrapy.Field()
    tags=scrapy.Field(
        input_processor=MapCompose(remove_comment_tags),#去掉tags中出現(xiàn)的評論
        output_processor=Join(",")#使用join把字符串連接起來偎球,原理同最開始的寫法
    )
    front_image_url=scrapy.Field(
        output_processor=MapCompose(return_value)#保證front_image_url里面的參數(shù)格式是list,,配置image pipeline之后洒扎,front_image_url傳入必須是list形式
    )
    front_image_path=scrapy.Field()#方便之后scrapy下載圖片辑甜,提取articleImagePipeline里面的results里面的path
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市胡诗,隨后出現(xiàn)的幾起案子邓线,更是在濱河造成了極大的恐慌,老刑警劉巖煌恢,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骇陈,死亡現(xiàn)場離奇詭異,居然都是意外死亡瑰抵,警方通過查閱死者的電腦和手機你雌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來二汛,“玉大人婿崭,你說我怎么就攤上這事‰燃眨” “怎么了氓栈?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長婿着。 經(jīng)常有香客問我豹芯,道長仙逻,這世上最難降的妖魔是什么诫肠? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任泄私,我火速辦了婚禮,結(jié)果婚禮上袜硫,老公的妹妹穿的比我還像新娘。我一直安慰自己挡篓,他們只是感情好婉陷,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著官研,像睡著了一般秽澳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上戏羽,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天担神,我揣著相機與錄音,去河邊找鬼始花。 笑死妄讯,一個胖子當著我的面吹牛孩锡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播亥贸,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼躬窜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炕置?” 一聲冷哼從身側(cè)響起荣挨,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎朴摊,沒想到半個月后默垄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡甚纲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年口锭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贩疙。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡讹弯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出这溅,到底是詐尸還是另有隱情组民,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布悲靴,位于F島的核電站臭胜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏癞尚。R本人自食惡果不足惜耸三,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浇揩。 院中可真熱鬧仪壮,春花似錦、人聲如沸胳徽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽养盗。三九已至缚陷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間往核,已是汗流浹背箫爷。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人虎锚。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓硫痰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翁都。 傳聞我的和親對象是個殘疾皇子碍论,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容