爬蟲實戰——Scrapy爬取伯樂在線所有文章


Scrapy簡單介紹及爬取伯樂在線所有文章

一.簡說安裝相關環境及依賴包

  1.安裝Python(2或3都行,我這里用的是3)

  2.虛擬環境搭建:

    依賴包:virtualenv,virtualenvwrapper(為了更方便管理和使用虛擬環境)

    安裝:pip install virtulaenv,virtualenvwrapper或通過源碼包安裝

    常用命令:mkvirtualenv --python=/usr/local/python3.5.3/bin/python article_spider(若有多個Python版本可以指定,然后創建虛擬環境article_spider);

         workon :顯示當前環境下所有虛擬環境

        workon 虛擬環境名:進入相關環境:

        退出虛擬環境:deactivate

        刪除虛擬環境:rmvirtualenv article_spider

        

    安裝相關依賴包及Scrapy框架:pip install scrapy(建議用豆瓣源鏡像安裝,快得多pip install https://pypi.douban.com /simple scrapy)   

                  windows操作環境中還需安裝(pip install pypiwin32)

                                                                 注:若安裝失敗有可能是版本不同,可以到官網查看對應版本安裝:https://www.lfd.uci.edu/~gohlke/pythonlibs/

  3.新建Scrapy項目(可以定制模板,這里用默認的):

    scrapy startproject article_spider:  

    用Pycharm打開,結構如下(與Django相似),爬蟲都放在spider文件夾中:

    創建爬蟲文件:cd article_spider:進入項目

           scrapy genspider --list(查看spider提供的模板)

           scrapy genspider -t 模板名 爬蟲文件名 域名(指定模板):

 

 

           scrapy genspider 爬蟲文件名  所爬取的域名(默認模板為basic)

 

    jobbole.py文件如下(start_url中的url都會通過parse函數,可以把要爬取的網址放進start_url):查看Spider源碼可知,通過start_requests返回url,是一個生成器

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


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

    def parse(self, response):
        pass

 

二.爬蟲相關技能介紹

  1.新建main函數,執行並調試爬蟲:

from scrapy.cmdline import execute
import sys
import os
#將父目錄添加到搜索目錄中
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
execute(["scrapy","crawl","jobbole"])

 

  修改setting.py:

# Obey robots.txt rules
#默認為True會過濾掉ROBOTS協議
ROBOTSTXT_OBEY =False

   調試結果如下,body中為網頁所有內容:

  

  3.scrapy shell的使用(方便調試):

    3.1scrapy shell  "http://blog.jobbole.com/114405/"(scrapy shell 要爬取調試的url)

 

    3.2xpath提取並獲取文章名,extract()方法將Selectorlist轉換為list:

  3.Xpath的使用,提取所需內容(比Beautifulsoup快得多):

    3.1xapth節點關系:

      父節點

      子節點

      同胞節點(兄弟節點)

      先輩節點

      后代節點

    3.2xpath語法簡單使用:

    3.3提取文章名:

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


class JobboleSpider(scrapy.Spider):
    name = 'jobbole'
    allowed_domains = ['blog.jobbole.com']
    start_urls = ['http://blog.jobbole.com/114405/']
def parse(self, response):
   title
=response.xpath('//*[@id="post-114405"]/div[1]/h1/text()')
  
pass

 

 

返回的是一個Selectorlist,便於嵌套xpath

  

    3.4xpath獲取時間:

    3.5獲取點贊數,xpath的contains函數,獲取class包含vote-post-up的span標簽下的:

    3.6獲取收藏數:

fav_nums=response.xpath("//span[contains(@class,'bookmark-btn')]/text()").extract()[0]
#使用正則匹配,有可能無收藏,匹配不到 match_fav
=re.match(".*(\d+).*",fav_nums) if match_fav: fav_nums=int(match_fav.group(1)) else: fav_nums=0

 

     3.7獲取評論數:

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

 

     3.8文章內容:

 

     3.9標簽提取:

 tag_list= response.xpath('//*[@id="post-114405"]/div[2]/p/a/text()').extract()
 tag_list=[element for element in tag_list if not element.strip().endswith("評論")]
 tags=','.join(tag_list)

 

  4.CSS選擇器篩選提取內容:

     4.1CSS常用方法:

    4.2獲取文章名:

 

    

    4.3獲取時間:

 

    4.4獲取點贊數:

 

 

    4.5獲取收藏數:

 

 

     4.6獲取評論數:

 

 

     4.7文章內容:

 

     4.8標簽提取:

 

  5.Xpath和CSS提取比較,哪種方便用哪個都行,extract()[0]可以換成extract_first("")直接提取第一個,無則返回空:

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

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

    def parse(self, response):
        #通過xpath提取
        title=response.xpath('//div[@class="entry-header"]/h1/text()')
        create_date= response.xpath('//p[@class="entry-meta-hide-on-mobile"]/text()').extract()[0].replace("·","").strip()
        praise_nums=response.xpath("//span[contains(@class,'vote-post-up')]/h10/text()").extract()[0]
        if praise_nums:
            praise_nums=int(praise_nums)
        else:
            praise_nums=0
        fav_nums=response.xpath("//span[contains(@class,'bookmark-btn')]/text()").extract()[0]
        match_fav=re.match(".*(\d+).*",fav_nums)
        if match_fav:
            fav_nums=int(match_fav.group(1))
        else:
            fav_nums=0
        comments_nums = response.xpath("//a[@href='#article-comment']/span/text()").extract()[0]
        math_comments=re.match(".*(\d).*",comments_nums)
        if math_comments:
            comments_nums=int(math_comments.group(1))
        else:
            comments_nums=0
        cotent=response.xpath('//div[@class="entry"]').extract()[0]
        tag_list= response.xpath('//div[@class="entry-meta"]/p/a/text()').extract()
        tag_list=[element for element in tag_list if not element.strip().endswith("評論")]
        tags=','.join(tag_list)

        #通過CSS提取
        title=response.css(".entry-header > h1::text").extract()[0]
        create_time=response.css("p.entry-meta-hide-on-mobile::text").extract()[0].replace("·","").strip()
        praise_nums=int(response.css("span.vote-post-up h10::text").extract()[0])
        if praise_nums:
            praise_nums = int(praise_nums)
        else:
            praise_nums = 0
        fav_nums=response.css(".bookmark-btn::text").extract()[0]
        match_fav = re.match(".*(\d+).*", fav_nums)
        if match_fav:
            fav_nums = int(match_fav.group(1))
        else:
            fav_nums = 0
        comments_nums=response.css("a[href='#article-comment'] span::text").extract()[0]
        math_comments = re.match(".*(\d).*", comments_nums)
        if math_comments:
            comments_nums = int(math_comments.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)

 

 三.具體實現

  1.獲取所有文章url(jobbole.py):

from scrapy.http import Request
#提取域名的函數
#python3
from urllib import parse
#python2
#import urlparse

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

    def parse(self, response):
        '''
        1.獲取文章列表頁中的文章url並交給scrapy下載后進行解析;
        2.獲取下一頁url並交給scrapy下載交給parse解析字段  
        '''
        #解析列表頁中所有文章url交給scrapy下載后進行解析
        post_urls=response.css("div#archive div.floated-thumb div.post-meta p a.archive-title::attr(href)").extract()
        for post_url in post_urls:
            #若提取的url不全,不包含域名,可以用parse拼接
            #Request(url=parse.urljoin(response.url,post_url),callback=self.parse_detail)
            #生成器,回調
            yield Request(post_url,callback=self.parse_detail)
        #提取下一頁並交給scrapy下載
        next_url=response.css(".next.page-numbers::attr(href)").extract_first()
        if next_url:
            yield Request(next_url,callback=self.parse)

    def parse_detail(self,response):
        # 通過xpath提取
        title=response.xpath('//div[@class="entry-header"]/h1/text()')
        create_date= response.xpath('//p[@class="entry-meta-hide-on-mobile"]/text()').extract()[0].replace("·","").strip()
        praise_nums=response.xpath("//span[contains(@class,'vote-post-up')]/h10/text()").extract()[0]
        if praise_nums:
            praise_nums=int(praise_nums)
        else:
            praise_nums=0
        fav_nums=response.xpath("//span[contains(@class,'bookmark-btn')]/text()").extract()[0]
        match_fav=re.match(".*(\d+).*",fav_nums)
        if match_fav:
            fav_nums=int(match_fav.group(1))
        else:
            fav_nums=0
        comments_nums = response.xpath("//a[@href='#article-comment']/span/text()").extract()[0]
        math_comments=re.match(".*(\d).*",comments_nums)
        if math_comments:
            comments_nums=int(math_comments.group(1))
        else:
            comments_nums=0
        cotent=response.xpath('//div[@class="entry"]').extract()[0]
        tag_list= response.xpath('//div[@class="entry-meta"]/p/a/text()').extract()
        tag_list=[element for element in tag_list if not element.strip().endswith("評論")]
        tags=','.join(tag_list)

   2.獲取文章封面圖:

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

    def parse(self, response):
        '''
        1.獲取文章列表頁中的文章url並交給scrapy下載后進行解析;
        2.獲取下一頁url並交給scrapy下載交給parse解析字段  
        '''
        #解析列表頁中所有文章url交給scrapy下載后進行解析
        #獲取url及image的節點
        post_nodes=response.css("div#archive div.floated-thumb div.post-thumb a")
        for post_node in post_nodes:
            image_url=post_node.css("img::attr(src)")
            post_url=post_node.css("::attr(href)")
            #若提取的url不全,不包含域名,可以用parse拼接
            #Request(url=parse.urljoin(response.url,post_url),callback=self.parse_detail)
            #生成器,回調
            yield Request(parse.urljoin(response.url,post_url),meta={"front-image-url":image_url},callback=self.parse_detail)
        #提取下一頁並交給scrapy下載
        next_url=response.css(".next.page-numbers::attr(href)").extract_first()
        if next_url:
            yield Request(next_url,callback=self.parse)
  def parse_detail(self,response):
  # 通過xpath提取
   front_image_url=response.meta.get("front-image-url","")#文章封面圖

 

  2.items.py(通過item實例化,類似於字典,但功能更全,可以集中管理,去重等):

    2.1源碼如下:

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

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class ArticleSpiderItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass

 

     2.2添加文章類,用來實例化提取的文章格式(類似於Django的Model)【items.py中】:

......
class JobboleArticleSpider(scrapy.Item):
    #字段中有Field類型,可以接受任何類型
    title=scrapy.Field()
    create_date=scrapy.Field()
    url=scrapy.Field()
    #對url做MD5,固定url的長度
    url_object_id=scrapy.Field()
    front_image_url=scrapy.Field()
    front_image_path=scrapy.Field()
    praise_nums=scrapy.Field()
    fav_nums=scrapy.Field()
    comment_nums=scrapy.Field()
    tags=scrapy.Field()
    content=scrapy.Field()

 

    2.3填充數據(jobbole.py中):

......  

def parse_detail(self,response): #實例化item中JobboleArtilce對象 article_item=JobboleArticleSpider() # 通過xpath提取 front_image_url=response.meta.get("front-image-url","")#文章封面圖 title=response.xpath('//div[@class="entry-header"]/h1/text()').extract_first() create_date= response.xpath('//p[@class="entry-meta-hide-on-mobile"]/text()').extract()[0].replace("·","").strip() praise_nums=response.xpath("//span[contains(@class,'vote-post-up')]/h10/text()").extract()[0] if praise_nums: praise_nums=int(praise_nums) else: praise_nums=0 fav_nums=response.xpath("//span[contains(@class,'bookmark-btn')]/text()").extract()[0] match_fav=re.match(".*(\d+).*",fav_nums) if match_fav: fav_nums=int(match_fav.group(1)) else: fav_nums=0 comments_nums = response.xpath("//a[@href='#article-comment']/span/text()").extract()[0] math_comments=re.match(".*(\d).*",comments_nums) if math_comments: comments_nums=int(math_comments.group(1)) else: comments_nums=0 content=response.xpath('//div[@class="entry"]').extract()[0] tag_list= response.xpath('//div[@class="entry-meta"]/p/a/text()').extract() tag_list=[element for element in tag_list if not element.strip().endswith("評論")] tags=','.join(tag_list) #填充item數據 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["fav_nums"]=fav_nums article_item["comment_nums"]=comments_nums article_item["tags"]=tags article_item["content"]=content #傳遞到item中 yield article_item

 

    2.4把setting.py中ITEM_PIPELINES注釋取消,讓其生效:

ITEM_PIPELINES = {
   'article_spider.pipelines.ArticleSpiderPipeline': 300,
}

     

調試發現,數據會傳送到pipelines中,可以在這兒做一系列操作

    2.5在setting.py中添加ImagesPipelines實現圖片自動下載(virtualenvs\article_spider\Lib\site-packages\scrapy\pipelines\images.py),下載圖片需要依賴PIL這個庫(pip install pillow):

ITEM_PIPELINES = {
   'article_spider.pipelines.ArticleSpiderPipeline': 300,
   #后面的數字大小表示pipeline先后流經的順序,先1,后300
   'scrapy.pipelines.images.ImagesPipeline':1
}
#配置圖片在item中下載的字段,front-image-url會被當成數組處理,因此jobole中該字段應改成列表
IMAGES_URLS_FIELD="front_image_url"
#配置圖片保存路徑
import os
#獲取setting.py的父目錄
project_dir=os.path.dirname(os.path.abspath(__file__))
IMAGES_STORE=os.path.join(project_dir,"images")
#設置圖片最小寬度,高度,即必須大於這么多才下載,還有很多屬性可以坎源碼
IMAGES_MIN_HEIGHT=100
IMAGES_MIN_WIDTH=100
 

 

 

front_image_url需轉換為列表格式才能被下載

部分image.py源碼 

    2.6定制pipeline處理封面圖,獲取圖片地址(pipelines.py中):

 

class ArticleImagePipeline(ImagesPipeline):
    '''
    定制圖片的pipepline
    '''
   #重寫ImagesPipeline中的item_completed()函數
    def item_completed(self, results, item, info):
        for ok, value in results:
            image_path = value['path']
        item['front_image_path'] = image_path
    #記得返回item
return item

 

 

 通過item_completed()獲取的results結構如上圖

item_completed()該方法源碼如上圖

    2.7使用md5固定url長度(Python3默認所有字符為unicode,而unicode不能被hash),可以單獨寫在一個py文件;里,新建utils下common.py:

import hashlib


def get_md5(url):
    # 判斷url如果為unicode編碼,則轉換為utf-8
    if isinstance(url, str):
        url = url.encode('utf-8')
    m = hashlib.md5()
    m.update(url)
    return m.hexdigest()


if __name__ == "__main__":
    print(get_md5("https://jobbole.com".encode('utf-8')))

 

 

導入模塊並調用get_md5()填充數據(jobbole.py)

  3.數據的保存:

    3.1通過json格式保存到文件(PIpelines.py,記得配置到setting中):

class JsonEncodingPipeline(object):
#自定義json文件的導出
def __init__(self): self.file=codecs.open("article.json","w",encoding='utf-8') def process_item(self, item, spider): #調用pipeline生成的函數,ensure_ascii=False防止中文等編碼錯誤 lines=json.dumps(dict(item),ensure_ascii=False)+"\n" self.file.write(lines) return item def spider_closed(self,spider): ''' 調用spider_closed(信號)關閉文件 ''' self.file.close()

 

ITEM_PIPELINES = {
   'article_spider.pipelines.ArticleSpiderPipeline': 300,
   #后面的數字大小表示pipeline先后流經的順序
   # 'scrapy.pipelines.images.ImagesPipeline':1
   'article_spider.pipelines.JsonEncodingPipeline':2,
   'article_spider.pipelines.ArticleImagePipeline':1
}

 

    3.2還可以用scrapy自帶的庫保存json格式(還有很多其他格式)(from scrapy.exporters import JsonItemExporter):

 

exporters中的文件格式種類

class JsonExporterPipeline(object):
    #調用scrapy提供的exporter導出json文件
    def __init__(self):
        self.file=open("articlexporter.json","wb")
        self.exporter=JsonItemExporter(self.file,encoding="utf-8",ensure_ascii=False)
#寫入"[\n" self.exporter.start_exporting()
def close_spider(self,spider):
    #寫入"]\n" self.exporter.finish_exporting() self.file.close()
def process_item(self, item, spider): self.exporter.export_item(item) return item

 

    3.3數據導入mysql:

      表的設計,這里只有一張表,可以直接設計:

 

      時間格式的轉換:

 try:
       create_date=datetime.datetime.strptime(create_date,"%Y/%m/%d").date()
 except Exception as e:
       create_date=datetime.datetime.now().date()
 article_item["create_date"]=create_date

       mysql驅動安裝:pip install mysqlclient,利用Mysqldb連接數據庫時的參數:

 

       mysqlpipeline的實現,第一種插入mysql的方法(記得setting中配置pipeline),解析速度大於數據插入mysql速度,有可能導致阻塞:

 

......
import Mysqldb
class
MysqlPipeline(object): #數據導入數據庫,爬取速度有可能遠大於插入速度,造成阻塞 def __init__(self): self.conn=MySQLdb.connect("localhost","root","112358","bole_articles",charset="utf8",use_unicode=True) self.cursor=self.conn.cursor() def process_item(self,item,spider): inser_sql=""" insert into articles(title,url,url_object_id,font_img_url,font_img_path,create_time,fa_num,sc_num,pinglun_num,tag,content) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) """ self.cursor.execute(inser_sql,(item["title"],item["url"],item["url_object_id"], item["front_image_url"],item["front_image_path"],item["create_date"], item["praise_nums"],item["fav_nums"],item["comment_nums"],item["tags"],item["content"])) self.conn.commit()

 

     mysqltwistedpipeline異步插入(基於Twisted異步框架):

   

#setting中配置mysql相關信息
MYSQL_HOST="localhost"
MYSQL_DBNAME="bole_articles"
MYSQL_USER="root"
MYSQL_PASSWORD="112358"

 

     

......
from twisted.enterprise import adbapi
import MySQLdb
import MySQLdb.cursors
class MysqlTwistedPipeline(object):
    def __init__(self, dbpool,dbpool2):
        self.dbpool = dbpool

    # 導入setting中的配置(固定函數),注:是from_settings而不是from_setting
    @classmethod
    def from_settings(cls, setting):
        # 將dbtool傳入
        dbparms = dict(
            host=setting["MYSQL_HOST"],
            db=setting["MYSQL_DBNAME"],
            user=setting["MYSQL_USER"],
            password=setting["MYSQL_PASSWORD"],
            charset="utf8",
            cursorclass=MySQLdb.cursors.DictCursor,
            use_unicode=True,
        )
        # twisted異步容器,使用MySQldb模塊連接
        dbpool = adbapi.ConnectionPool("MySQLdb", **dbparms)

        return cls(dbpool)

    def process_item(self, item, spider):
        # 使用Twisted將mysql插入變成異步執行
        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):
        # 執行具體的插入
        inser_sql = """
               insert into articles(title,url,url_object_id,font_img_url,font_img_path,create_time,fa_num,sc_num,pinglun_num,tag,content)
               VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
               """
        cursor.execute(inser_sql, (item["title"], item["url"], item["url_object_id"],
                                   item["front_image_url"], item["front_image_path"], item["create_date"],
                                   item["praise_nums"], item["fav_nums"], item["comment_nums"], item["tags"],
                                   item["content"]))

 

   4.item_loader的使用(jobbole.py):代碼更加簡潔,但值全為list,定制需要修改item中函數

from scrapy.loader import ItemLoader
...... 
def parse_detail(self,response):
 #通過item_loader加載item
        item_loader=ItemLoader(item=JobboleArticleSpider(),response=response)
        #三個重要方法item_loader.add_xpath();item_loader.add_css();item_loader.add_css()
        item_loader.add_css("title",".entry-header > h1::text")
        item_loader.add_value("url",response.url)
        item_loader.add_value("url_ooject_id",get_md5(front_image_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","span.vote-post-up h10::text")
        item_loader.add_css("fav_nums",".bookmark-btn::text")
        item_loader.add_css("comments_nums", "a[href='#article-comment'] span::text")
        item_loader.add_css("content", "div.entry")
        item_loader.add_css("tags", "p.entry-meta-hide-on-mobile a::text")
     #調用此方法才生效
       article_item=item_loader.load_item()
        yield article_item

 

   5.修改item處理得到的函數:

from scrapy.loader.processors import MapCompose,TakeFirst
......

def date_convert(value):
    #定義處理時間函數,返回時間
    try:
        create_date = datetime.datetime.strptime(value, "%Y/%m/%d").date()
    except Exception as e:
        create_date = datetime.datetime.now().date()
    return create_date


class JobboleArticleSpider(scrapy.Item):
    # 字段中有Field類型,可以接受任何類型
    title = scrapy.Field(
    #可以傳多個函數 input_processor
=MapCompose(lambda x: x + "hah") ) create_date = scrapy.Field( #處理時間,還是數組 input_processor=MapCompose(date_convert), #只取數組的第一個,如果都要寫麻煩,可以定制itemloader output_processor=TakeFirst() ) url = scrapy.Field() # 對url做MD5,固定url的長度 url_object_id = scrapy.Field() front_image_url = scrapy.Field() front_image_path = scrapy.Field() praise_nums = scrapy.Field() fav_nums = scrapy.Field() comment_nums = scrapy.Field() tags = scrapy.Field() content = scrapy.Field()

 

 

   6.定制itemloader:

items,py:

from scrapy.loader import ItemLoader
......
class ArticleItemLoader(ItemLoader):
    #自定義itemloader,值取數組的第一個,修改item中的loader
    default_output_processor = TakeFirst()

 

 jobbole.py:

......
 item_loader=ArticleItemLoader(item=JobboleArticleSpider(),response=response)
        #三個重要方法item_loader.add_xpath();item_loader.add_css();item_loader.add_css()
        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(front_image_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","span.vote-post-up h10::text")
        item_loader.add_css("fav_nums",".bookmark-btn::text")
        item_loader.add_css("comment_nums", "a[href='#article-comment'] span::text")
        item_loader.add_css("content", "div.entry")
        item_loader.add_css("tags", "p.entry-meta-hide-on-mobile a::text")

        article_item=item_loader.load_item()
        yield article_item

 

用itemloader方法定制的item(items.py):

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

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html
import datetime
import re

import scrapy
# TakeFirst取第一個,Join連接
from scrapy.loader.processors import MapCompose, TakeFirst, Join
from scrapy.loader import ItemLoader


class ArticleSpiderItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass


def date_convert(value):
    # 定義處理時間函數,返回時間
    try:
        create_date = datetime.datetime.strptime(value, "%Y/%m/%d").date()
    except Exception as e:
        create_date = datetime.datetime.now().date()
    return create_date


class ArticleItemLoader(ItemLoader):
    # 自定義itemloader,值取數組的第一個,修改item中的loader
    default_output_processor = TakeFirst()


def get_nums(value):
    # 定義處理點贊數,收藏數,評論數處理等
    match_num = re.match(".*(\d+).*", value)
    if match_num:
        value = int(match_num.group(1))
    else:
        value = 0
    return value


def return_value(value):
    # 什么也不做
    return value


def remove_comment(value):
    # 去掉tag中提取的含評論的便簽
    if "評論" in value:
        return ""
    else:
        return value


class JobboleArticleSpider(scrapy.Item):
    # 字段中有Field類型,可以接受任何類型
    title = scrapy.Field()
    create_date = scrapy.Field(
        # 處理時間,還是數組
        input_processor=MapCompose(date_convert),
        # 只取數組的第一個
        # output_processor=TakeFirst()
    )
    url = scrapy.Field()
    # 對url做MD5,固定url的長度
    url_object_id = scrapy.Field()
    front_image_url = scrapy.Field(
  #覆蓋定制的itemloader,這里必須為列表 outout_processor
=MapCompose(return_value) ) front_image_path = scrapy.Field() praise_nums = scrapy.Field( input_processor=MapCompose(get_nums) ) fav_nums = scrapy.Field( input_processor=MapCompose(get_nums) ) comment_nums = scrapy.Field( input_processor=MapCompose(get_nums) ) tags = scrapy.Field( # 覆蓋定制的取第一個 output_processor=Join(",") ) content = scrapy.Field()

 

四.總結

  利用上面的方法就可以快速爬取所有文章了,scrapy是一個分布式的設計,也是多線程,寫爬蟲的主要部分就是在spider中定制爬蟲要爬取的url及填充數據(jobbole.py),以及定制item的模板(items.py),然后就是定制pipeline對item中的數據進行一系列操作,如寫入json文件,導入數據庫,下載圖片,獲取圖片路徑等等。

      

 

 

 

  

           

 

                

        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM