Scrapy分布式爬虫---爬取伯乐在线所有文章

—爬取伯乐在线所有文章—

1,scrapy安装及目录结构介绍

  • 电脑的基础配置,需要的开发工具
1.python 3.5.3
2.PyCharm 2016.3
3.mysql+navicat for mysql
  • 基础虚拟环境的搭建和配置
pip install virtualenv
pip install virtualenvwrapper-win
安装虚拟环境管理
mkvirtualenv article_spider
创建虚拟环境
workon articles_spider
直接进入虚拟环境
deactivate
退出激活状态
workon
知道有哪些虚拟环境
  • 命令行创建scrapy项目
pip install -i https://pypi.douban.com/simple/ scrapy
在虚拟环境下使用豆瓣源安装scrapy
scrapy startproject articlespider
创建scrapy项目
  • scrapy目录结构

scrapy.cfg

scrapy.cfg:配置文件.   scrapy借鉴了django的项目思想

settings.py:关于scrapy的配置

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

pipelines.py:做跟数据存储相关的文件
middlewares.py:自己自定义middlewares,让scrapy变得更加可控
items.py:定义数据保存的格式,保存我们所爬取到的数据

  • 创建我们的spider: jobbole.py
workon article_spider
scrapy genspider jobbole blog.jobbole.com

可以看到直接为我们创建好的空项目里已经有了模板代码。如下:

# -*- 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函数里面的逻辑是我们需要写的东西
          pass
  • scrapy执行流程

在命令行中启动刚刚创建的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解决

创建我们的调试工具类:在项目根目录里创建main.py作为调试工具文件

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

import sys
import os

#将系统当前目录设置为项目根目录
os.path.abspath(__file__)#为当前文件所在绝对路径-articlespider
os.path.dirname#为文件所在目录-D:\LinuxShare\

sys.path.append(os.path.dirname(os.path.abspath(__file__)))
#执行命令,相当于在控制台cmd输入改名了
execute(["scrapy", "crawl" , "jobbole"])#执行命令scrapy crawl jobbole

settings.py里面的设置要不遵循reboots协议,防止scrapy过滤掉不符合reboots协议的url

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

在jobble.py打上断点:

def parse(self, response):
      pass#打断点处

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

《Scrapy分布式爬虫---爬取伯乐在线所有文章》 response.PNG

对象内部中的body是整个网页源文件的内容,而且scrapy帮我们做了编码转换为utf-8格式。

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

2,提取伯乐在线内容

  • xpath的使用

为什么要使用xpath?
1.xpath使用路径表达式在xml和html中进行导航
2.xpath包含一个标准函数库
3.xpath是一个w3c标准
4.xpath速度快于beautifulsoup

xpath节点的关系:
1.父节点 2.子节点 3.同胞节点 4.先辈节点 5.后代节点

xpath语法:

《Scrapy分布式爬虫---爬取伯乐在线所有文章》 xpath语法(一).PNG

《Scrapy分布式爬虫---爬取伯乐在线所有文章》 xpath语法–谓语.PNG

《Scrapy分布式爬虫---爬取伯乐在线所有文章》 xpath语法(二).PNG

获取一个网页上元素的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脚本中进行调试

(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("//p[@class='entry-meta-hide-on-mobile']/text()").extract()[0].strip().replace("·","").strip()
        praise_nums=response.xpath("//span[contains(@class,'vote-post-up')]/h10/text()").extract()[0]

        fav_nums=response.xpath("//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("//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("//div[@class='entry']").extract()[0]#取全文内容
        create_date = response.xpath("//p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
        tag_list=response.xpath("//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下载网页
from scrapy.http import Request#把Request里面的url交给scrapy
...
Request(url=post_url,callback=self.parse_detail)
  • parse函数完成url拼接防止出现href元素内网址不全的情况
from urllib import parse#url可能出现无域名的情况,这时候引用函数parse
...
Request(url=parse.urljoin(response.url,post_url),callback=self.parse_detail)
  • yield关键字
#使用request下载详情页面,下载完成后回调方法parse_detail()提取文章内容中的字段
yield Request(url=parse.urljoin(response.url,post_url),callback=self.parse_detail)
#yield关键字用于把Request提取到的内容交给scrapy进行下载
  • 提取“下一页”的url
next_urls=response.css(".next.page-numbers::attr(href)").extract()[0]#next 和 page-numbers是两个class,这样表示这个节点有next和page
        if next_urls:
            yield Request(url=parse.urljoin(response.url,next_urls),callback=self.parse)
#注意callback=self.parse这里传入parse函数并没有进行调用
  • 实现全部文章字段遍历和解析的代码如下:
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下载完成之后,回调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)
  • 全部文章字段解析和下载的逻辑流程图:

《Scrapy分布式爬虫---爬取伯乐在线所有文章》 全部文章的逻辑流程图.png

4,scrapy的items整合字段

  • 数据爬取的任务就是从非结构的数据中提取出结构性的数据。
    items 可以让我们自定义返回自己所提取的字段(类似于字典,但比字典的功能更齐全),我们可以将实例化的items直接yield,这样scrapy就可以帮我们把items路由到pipelines中,方便这些字段数据集中在pipelines中的保存,去重等等

  • 获取列表页封面图的url,并通过Request传到response里面,在文章详情页中可能没有封面图的url,所以要在下载网页时把这个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

    《Scrapy分布式爬虫---爬取伯乐在线所有文章》 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,数字越小表示越优先进入。
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设置成数组格式,安装pillow库。

  • 自定义articleImagePipeline用来存放获取的封面图

from scrapy.pipelines.images import ImagesPipeline
...
class articleImagePipeline(ImagesPipeline):#继承ImagesPipeline,只用来处理封面图的自定义的pipeline,
    def item_completed(self,results,item,info):
        pass
  • 在函数item_completed下的result下的字典dict下获取到图片保存的地址path

    《Scrapy分布式爬虫---爬取伯乐在线所有文章》 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函数


#定义一个md5函数
def get_md5(url):
    if isinstance(url,str):
        url=url.encode("utf-8")#判断url是否为unicode,如果是,转成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,所以要转成encode
  • 在jobbole.py中将md5保存下来
from articlespider.utils.common import get_md5
...
article_item["url_object_id"]=get_md5(response.url)

5,数据保存到本地文件及mysql中

  • 保存item到json文件中
    import codecs使打开文件时避免了一些编码问题,自定义JsonWithEncodingPipeline实现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要转换成dict才能dumps
        self.file.write(lines)#写入article.json当中
        return item

    def spider_closed(self,spider):#当出现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文件发现生成了article.json文件

  • 调用scrapy提供的json export导出json文件
from scrapy.exporters import JsonItemExporter
...
class JsonExporterPipleline(object):
    #调用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文件发现生成了articleexport.json文件

  • 数据表设计

《Scrapy分布式爬虫---爬取伯乐在线所有文章》 数据表.PNG

注意:三个num字段不能设置为空,默认值为零;
如果设置url_object_id为主键,则必须在代码中进行插入操作。

  • 通过pipeline保存数据到mysql

数据库驱动安装

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

因为我们爬取网页url的速度后期可能会大于数据库存储的速度,采用twisted框架可以让我们数据库的插入变成一种异步的操作。

在settings.py中设置可配置参数:

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(#传入的参数要和MySQLdb.connect里面的connection下面的参数一致
            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是要传入的参数
        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):
        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提供了一个容器,让我们配置提取字段的规则:
add_xpath,add_css,add_value

from scrapy.loader import ItemLoader
...
#通过Item Loader加载item,scrapy提供的提取字段的配置函数
        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中

《Scrapy分布式爬虫---爬取伯乐在线所有文章》 itemloader.PNG

会发现所有值变成了list,因此需要处理函数来对这些值进行过滤。

MapCompose可以传入函数对于该字段进行处理,而且可以传入多个:

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()#此函数用来取数组中的第一个数据,但是是str类型

保存[front_image_url]的list值:

def return_value(value):#front_image_url调用
    return value
...
class JobBolearticleItem(scrapy.Item):
      front_image_url=scrapy.Field(
        output_processor=MapCompose(return_value)#保证front_image_url里面的参数格式是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中出现的评论
        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里面有两个参数,list里面有tuple和dict,dict里面的path是image保存的文件的路径
        if "front_image_url" in item:#知乎等网站可能没有front_image_url
            for ok, value in results:
                image_file_path = value["path"]
            item["front_image_path"] = image_file_path

        return item

自定义的item带处理函数的完整代码:

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中出现的评论
        output_processor=Join(",")#使用join把字符串连接起来,原理同最开始的写法
    )
    front_image_url=scrapy.Field(
        output_processor=MapCompose(return_value)#保证front_image_url里面的参数格式是list,,配置image pipeline之后,front_image_url传入必须是list形式
    )
    front_image_path=scrapy.Field()#方便之后scrapy下载图片,提取articleImagePipeline里面的results里面的path
    原文作者:这辈子不发微博只发简书
    原文地址: https://www.jianshu.com/p/f86659aef1e0
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞