scrapy的基本使用
pip install -i https://pypi.douban.com/simple/ scrapy
- 新建一个scrapy的项目,在cmd中,工作路径中
- scrapy startproject ArticleSpider | 创建一个项目
- 然后在pycharm中打开,可以看到一些基本的配置信息
- cd ArticleSpider(项目名)
- scrapy genspider jobbole blog.jobbole.com | 创建一个模板,指向这个域名
- 此时在pycharm中就可以看到默认生成的代码(jobbole.py)
# -*- 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
虚拟环境的配置.png
- 启动,
- 在cmd中scrapy项目的目录路径下输入
scrapy crawl jobbole
,这个名字和创建时候的名字一样, - 此时或报错,说缺少'win32api',安装
pip install -i https://pypi.douban.com/simple pypiwin32
,再此运行就可以了 - 但是每次都打开cmd太麻烦了,此时设置一个scrapy的小脚本,这样方便运行,在根目录中新建一个main.py
- 在cmd中scrapy项目的目录路径下输入
# -*- coding: utf-8 -*-
from scrapy.cmdline import execute
import sys
import os
# 首先获取到当前文件的目录os.path.abspath(__file__)
# 然后os.path.dirname()获取到当前文件的上一节目录,也就是项目的目录
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# 然后执行命令,注意,里面传入的是一个数组
execute(["scrapy","crawl","jobbole"])
* 注意此时要把
* **settings.py文件中的ROBOTSTXT_OBEY属性设置为False**
* **pipelines.py是用于存储数据相关的东西**
* **middilewares.py是自定义的middilewares方法,处理响应的IO的操作**
* **init.py是项目的初始化文件**
* **items.py是我们要爬取的信息的相关属性,类似于表单,用来保存获取到的数据**
xpath提取数据
-
xpath语法
-
简单调试,在cmd当前的项目路径下,后面加要爬取的网址
调试.png
scrapy shell http://blog.jobbole.com/113912/
注意其中的extract()方法,提取数据,若数组为空会报异常、或者使用extract_first()方法提起第一个数据,就不会报异常 -
然后写入到pycharm中,在jobbole.py文件中
# -*- coding: utf-8 -*-
import scrapy
import re
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']
# 要爬取的连接,这里只爬取一页的数据
# start_urls = ['http://blog.jobbole.com/']
start_urls = ['http://blog.jobbole.com/113821/']
# 这里的response就自带xpath,不用像之前那样,在这直接获取想要的值就可以了
def parse(self, response):
# 标题
title = response.xpath('//*[@id="post-113912"]/div[1]/h1/text()').extract_first()
# 创建的时候
create_time = response.xpath('//p[@class="entry-meta-hide-on-mobile"]/text()').extract_first().strip().replace("·","").strip()
# 此时使用了contains函数,代表要找一个class(第一个参数),属性里面包括某个值(第二个参数)
# 点赞数
praise_nums = int(response.xpath("//span[contains(@class,' vote-post-up')]/h10/text()").extract_first())
# 使用这个就可以获取到收藏数,但是结果是这种格式,' 3 收藏',需要进行转换,使用正则
# response.xpath("//span[contains(@class,'bookmark-btn')]/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:
match_re = 0
# 评论数,这个和上面的一样,获取到的也是需要用正则 ' 1 评论'
comment_nums = response.xpath("//a[@href='#article-comment']//span/text()").extract()[0]
match_re = re.match(".*?(\d+).*", comment_nums)
if match_re:
comment_nums = int(match_re.group(1))
else:
comment_nums = 0
# 这里把文章的信息全部取出来
content = response.xpath("//div[@class='entry']").extract()[0]
# 获取标签信息 ['IT技术', ' 1 评论 '],这里把评论结尾的进行过滤
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)
pass
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/']
start_urls = ['http://blog.jobbole.com/113821/']
def parse(self, response):
# ::text打印出文字
title = response.css(".entry-header h1::text").extract()[0]
create_time = response.css(".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(".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
comment_nums = response.css("a[href='#article-comment']::text").extract()[0]
match_re = re.match(".*?(\d+).*", comment_nums)
if match_re:
comment_nums = int(match_re.group(1))
else:
comment_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)
pass
获取全部文章的url
- 放入主页面,首先获取到主页上每个文章的url,通过Request方法,callback=方法名,通过yield就一直解析
- 获取到下一页的链接,还是通过Request方法进行解析,调用的自身方法,通过yield就一直执行,直到最后的页面为空就结束了
# -*- coding: utf-8 -*-
import scrapy
import re
# 下载网页
from scrapy.http import Request
# parse拼接网址应对herf内有可能网址不全
from urllib import parse
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函数
:param response:
:return:
"""
# 解析列表页中文章的url并交给scrapy下载后进行解析
post_urls = response.css("#archive .floated-thumb .post-thumb a::attr(href)").extract()
for post_url in post_urls:
# 这里使用自带的这个方法,第一个参数是url,第二个参数是要执行的方法,这里传入方法名就可以。然后就会不断的执行这个方法
# 使用parse是为域名补全,若获取的域名不全的话使用这个方法就极为简单,就不用再进行拼接了
yield Request(url=parse.urljoin(response.url,post_url),callback=self.parse_detail)
# 提取下一页并交给scrapy进行下载
next_url = response.css(".next.page-numbers::attr(href)").extract_first("")
if next_url:
# 若下一页还存在的话,就再执行自身这个方法
yield Request(url=parse.urljoin(response.url,next_url),callback=self.parse)
# 提取文章的具体逻辑
def parse_detail(self,response):
# ::text打印出文字
title = response.css(".entry-header h1::text").extract_first()
create_time = response.css(".entry-meta-hide-on-mobile::text").extract_first().strip().replace(" ·","").strip()
praise_nums = response.css(".vote-post-up h10::text").extract_first()
fav_nums = response.css(".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
comment_nums = response.css("a[href='#article-comment'] span::text").extract()[0]
match_re = re.match(".*?(\d+).*", comment_nums)
if match_re:
comment_nums = int(match_re.group(1))
else:
comment_nums = 0
content = response.css("div.entry").extract_first()
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)
pass
数据处理
数据爬取的任务就是从非结构的数据中提取出结构性的数据。
items 可以让我们自定义自己的字段(类似于字典,但比字典的功能更齐全)
- 若文章有封面图该怎么获取呢,当前的写法
post_urls = response.css("#archive .floated-thumb .post-thumb a::attr(href)").extract()
改进后
post_nodes = response.css("#archive .floated-thumb .post-thumb a")
for post_node in post_nodes:
# 获取文章的封面图
image_url = post_node.css("img::attr(src)").extract_first("")
post_url = post_node.css("::attr(href)").extract_first("")
在下载网页的时候把获取到的封面图的url传给parse_detail的response,通过meta将它发送出去
post_nodes = response.css("#archive .floated-thumb .post-thumb a")
for post_node in post_nodes:
image_url = post_node.css("img::attr(src)").extract_first("")
post_url = post_node.css("::attr(href)").extract_first("")
# 这里meat参数是向resposne里传递参数,是一个字典,这样在parse_detail方法中就可以接收这个参数了
yield Request(url=parse.urljoin(response.url,post_url),meta={"front_image_url":image_url},callback=self.parse_detail)
在parse_detail函数中再接收这个数据
# 获取封面图片,注意这个是从Request中传递过来的
front_image_url = response.meta.get("front_image_url","")
这样就可以获取到文章的封面图了
- items.py:自定义要获取数据,这样更加的方便
import scrapy
class ArticlespiderItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
# 自定义item并在jobboled.py中进行填充
class JobBoleArticleItem(scrapy.Item):
# 定义Field()属性,这样这个title就可以传递任何类型的数据了
title = scrapy.Field()
create_date = scrapy.Field()
url = scrapy.Field()
# 文章链接进行MD5
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()
content = scrapy.Field()
tags = scrapy.Field()
- 在jobboled.py中进行填充
# -*- coding: utf-8 -*-
from ArticleSpider.items import JobBoleArticleItem
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']
start_urls = ['http://blog.jobbole.com/all-posts/']
def parse(self, response):
post_nodes = response.css("#archive .floated-thumb .post-thumb a")
for post_node in post_nodes:
image_url = post_node.css("img::attr(src)").extract_first("")
post_url = post_node.css("::attr(href)").extract_first("")
yield Request(url=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(url=parse.urljoin(response.url,next_url),callback=self.parse)
# 提取文章的具体逻辑
def parse_detail(self,response):
# 首先实例化自定义的item
article_item = JobBoleArticleItem()
# 获取封面图片,注意这个是从Request中传递过来的
front_image_url = response.mate.get("front_image_url","")
title = response.css(".entry-header h1::text").extract_first()
create_date = response.css(".entry-meta-hide-on-mobile::text").extract_first().strip().replace(" ·","").strip()
praise_nums = response.css(".vote-post-up h10::text").extract_first()
fav_nums = response.css(".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
comment_nums = response.css("a[href='#article-comment'] span::text").extract()[0]
match_re = re.match(".*?(\d+).*", comment_nums)
if match_re:
comment_nums = int(match_re.group(1))
else:
comment_nums = 0
content = response.css("div.entry").extract_first()
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)
# 进行填充
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'] = comment_nums
article_item['content'] = content
article_item['tags'] = tags
# 这里调用yield之后,这个article_item就会传递到 pipelines.py中
yield article_item
- yield article_item将这个item传送到pipelines中
- 首先在setting.py中将ITEM_PIPELINES注释去掉
ITEM_PIPELINES = {
'ArticleSpider.pipelines.ArticlespiderPipeline': 300,
}
这个时候item传输到pipeline就可以将其保存到数据库中的了
* setting设置下载图片
ITEM_PIPELINES = {
'ArticleSpider.pipelines.ArticlespiderPipeline': 300,
'scrapy.pipeline.images.ImagesPipeline':1,
}
* 然后定义下载那个图片以及下载图片的地址,还可以对下载的图片进行约束
注意,后面的数字代表优先级,当有多个事件的时候,数值越小,优先级就越高,就越先执行
ITEM_PIPELINES = {
'ArticleSpider.pipelines.ArticlespiderPipeline': 300,
'scrapy.pipeline.images.ImagesPipeline':1,
}
# 下载图片的时候要自己配置下载图片的url,这个就是item中获取到的图片的封面
IMAGES_URLS_FIELD = "front_image_url"
# 设置图片的保存路径相对路径或者结对路径
# 获取到当前文件的上一级目录
project_dir = os.path.abspath(os.path.dirname(__file__))
# 使用这个方法获取到images的目录,图片保存到这里
IMAGES_STORE = os.path.join(project_dir,"images")
# 对下载的图片进行约束
# IMAGES_MIN_HEIGHT = 100
# IMAGES_MIN_WIDTH = 100
注意:安装PIL pip install pillow
* 定制自己的pipeline使其下载图片后能保存下它的本地路径,在pipeline.py文件中
# -*- coding: utf-8 -*-
from scrapy.pipelines.images import ImagesPipeline
..
#保持不变就可以
# 自定义的图片处理
class ArticlesImagePipeline(ImagesPipeline):
# 继承自ImagesPipeline,然后重写这个方法,可以获取到图片实际下载地址
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中设置自定义的pipline,
ITEM_PIPELINES = {
'ArticleSpider.pipelines.ArticlespiderPipeline': 300,
# 'scrapy.pipeline.images.ImagesPipeline':1,
'ArticleSpider.pipelines.ArticlesImagePipeline':1,
}
* url的md5处理,新建一个utils文件夹,键一个common.py文件
# -*- coding: utf-8 -*-
import hashlib
def get_md5(url):
# 注意,这里传入的如果是字符串(unicode)要转为utf-8
if isinstance(url,str):
url = url.encode("utf-8")
m = hashlib.md5()
m.update(url)
return m.hexdigest()
然后在jobbole.py中将url的md5保存下来
from ArticleSpider.utils.common import get_md5
def parse_detail(self,response):
..
article_item['url_object_id'] = get_md5(response.url)
数据保存
- 保存到本地json文件(自定义)
- 使用了
import codecs
打开文件避免了一些编码问题 - 在pipelines.py中自定义一个JsonWithEncodingPipeline来实现
- 使用了
import codecs
import json
class JsonWithEncodingPipeline(object):
# 初始化的时候打开一个json文件,注意这里使用了codecs,
def __init__(self):
self.file = codecs.open('article.json','w',encoding='utf-8')
# 这是默认的数据处理方法
def process_item(self,item,spider):
# 首先把这个item数据转换为json数据,这里要注意ensure_ascii设置为false,为了避免中文乱码,每一天数据加一个换行符
lines = json.dumps(dict(item),ensure_ascii=False) + '\n'
# 然后写入到指定的文件中
self.file.write(lines)
return item
# 当spider关闭的时候就会调用这个方法
def spider_closed(self,spider):
self.file.close()
* 在setting.py中注册
ITEM_PIPELINES = {
'ArticleSpider.pipelines.JsonWithEncodingPipeline': 2,
# 'scrapy.pipeline.images.ImagesPipeline':1,
'ArticleSpider.pipelines.ArticlesImagePipeline':1,
}
* 运行程序就可以看到![保存本地json文件.png](https://img.haomeiwen.com/i2840691/679f7156eebdd117.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 使用scrapy内置的方法导出json数据
- 导入
from scrapy.exporters import JsonItemExporter
- 在pipelines.py中自定义一个类来实现
- 导入
from scrapy.exporters import JsonItemExporter
class JsonExporterPipleline(object):
# 调用scrapy提供的json export导出json文件
def __init__(self):
self.file = open('articleexport.json','wb')
self.export = JsonItemExporter(self.file,encoding="utf-8",ensure_ascii=False)
self.export.start_exporting()
def close_spider(self,spider):
self.export.finish_exporting()
self.file.close()
def process_item(self, item, spider):
self.export.export_item(item)
return item
* 在setting.py中注册
ITEM_PIPELINES = {
'ArticleSpider.pipelines.JsonExporterPipleline': 2,
# 'scrapy.pipeline.images.ImagesPipeline':1,
'ArticleSpider.pipelines.ArticlesImagePipeline':1,
}
- 保存数据到mysql中
- 根据items中建立对应的数据表,但是在保存数据的时候日期用的是字符串,这个时候在还要进行日期的转换,在jobbole.py中
import datetime
def parse_detail(self,response):
..
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
* 数据表设计
* 三个num字段均设置不能为空,然后默认0,content设置为longtext,主键设置为url_object_id
jobble数据表设计.png
* 安装数据库驱动
pip install mysqlclient
* 保存到数据库pipeline(同步)编写
import MySQLdb
class MysqlPipline(object):
# 类实例化的时候连接数据库
def __init__(self):
self.conn = MySQLdb.connect('127.0.0.1','root','123456','muke',charset="utf8",use_unicode=True)
# 执行sql语句
self.cursor = self.conn.cursor()
# 重写这个方法,这里做个演示,插入了少量的数据
def process_item(self, item, spider):
insert_sql = """
insert into jobbole_article(title,url,create_date,fav_nums) VALUES (%s,%s,%s,%s)
"""
self.cursor.execute(insert_sql,(item["title"],item["url"],item["create_date"],item["fav_nums"]))
self.conn.commit()
这个别忘了
ITEM_PIPELINES = {
'ArticleSpider.pipelines.MysqlPipline':1,
}
* 保存到数据库的(异步Twisted)编写,因为我们的爬取速度可能大于数据库存储的速度,异步操作。
* 设置参数:在setting.py中设置
MYSQL_HOST = '127.0.0.1'
MYSQL_DBNAME = 'muke'
MYSQL_USER = 'root'
MYSQL_PASSWORD = '123456'
* 还是在pipeline.py文件中,
import MySQLdb.cursors
from twisted.enterprise import adbapi
class MysqlTwistedPipline(object):
def __init__(self,dbpool):
self.dbpool = dbpool
# 通过这个方法就可以获取到在settings总的定义的参数
@classmethod
def from_settings(cls,settings):
# 这里注意顺序
dbparms = dict(
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)
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)
# 其他的基本不变,主要在这里执行不同的sql语句
def do_insert(self,cursor,item):
# 执行具体的插入逻辑
insert_sql = """
insert into jobbole_article(title,url,create_date,fav_nums) VALUES (%s,%s,%s,%s)
"""
cursor.execute(insert_sql, (item["title"], item["url"], item["create_date"], item["fav_nums"]))
这个别忘了
ITEM_PIPELINES = {
'ArticleSpider.pipelines.MysqlTwistedPipline':1,
}
- 这样就使用了异步的方式来保存数据
ItemLoader
- itemloadr提供了一个容器,让我们配置某一个字段该使用哪种规则
from scrapy.loader import ItemLoader
def parse_detail(self,response):
# 获取封面图片,注意这个是从Request中传递过来的
front_image_url = response.meta.get("front_image_url","")
# 使用自定义的Loader
# 通过ItemLoader加载item,最重要的两个参数,第一个是自定义的item
item_loader = ItemLoader(item=JobBoleArticleItem(),response=response)
# item中重要的方法
# item_loader.add_css()
# item_loader.add_xpath()
# item_loader.add_value()
# 第一个参数就是对应的自定义的item中的字段,第二个就是提取的规则
item_loader.add_css('title','.entry-header h1::text')
item_loader.add_css('create_date','.entry-meta-hide-on-mobile::text')
item_loader.add_css('praise_nums','.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',"a[href='#article-comment'] span::text")
item_loader.add_css('tags',"p.entry-meta-hide-on-mobile a::text")
# add_value()这个就是直接添加值
item_loader.add_value('url',response.url)
item_loader.add_value('url_object_id',get_md5(response.url))
#调用这个方法来对规则进行解析生成item对象
article_item = item_loader.load_item()
# 这里调用yield之后,这个article_item就会传递到 pipelines.py中
yield article_item
直接使用itemloader的问题.png
- 所有的值变成了list
- 对这些值要做一些处理函数( input_processor = MapCompose())
- 在item.py中进行处理,
from scrapy.loader.processors import MapCompose
class ArticlespiderItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
class JobBoleArticleItem(scrapy.Item):
# 定义Field()属性,这样这个title就可以传递任何类型的数据了,常用的两个参数
title = scrapy.Field(
# 当这个值传进来的时候,可以进行预处理,在方法中,可以添加处理多个函数,从左到右执行
input_processor = MapCompose()
)
* 自定义itemloader实现默认提取第一个
from scrapy.loader import ItemLoader
class ArticleItemLoader(ItemLoader):
# 自定义itemloader实现默认提取第一个
default_output_processor = TakeFirst()
* 此时在jobbole.py中要使用我们自定义的loader
from ArticleSpider.items import JobBoleArticleItem,ArticleItemLoader
item_loader = ArticleItemLoader(item=JobBoleArticleItem(),response=response)
* 下载图片pipeline增加if增强通用性
class ArticleImagePipeline(ImagesPipeline):
#重写该方法可从result中获取到图片的实际下载地址
def item_completed(self, results, item, info):
if "front_image_url" in item:
for ok, value in results:
image_file_path = value["path"]
item["front_image_path"] = image_file_path
return item
- items.py全部代码
# -*- coding: utf-8 -*-
import scrapy
from scrapy.loader import ItemLoader
from scrapy.loader.processors import MapCompose,TakeFirst,Join
import datetime
import re
class ArticlespiderItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
class ArticleItemLoader(ItemLoader):
# 自定义itemloader实现默认提取第一个
default_output_processor = TakeFirst()
# 这里传入value就是获取到的参数,日期处理
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
# 评论数等处理
def get_nums(value):
match_re = re.match(".*?(\d+).*", value)
if match_re:
nums = int(match_re.group(1))
else:
nums = 0
return nums
def remove_comment_tags(value):
# 去掉tag中提起的评论
if "评论" in value:
return ""
else:
return value
# list保存原值
def return_value(value):
return value
class JobBoleArticleItem(scrapy.Item):
# 定义Field()属性,这样这个title就可以传递任何类型的数据了,常用的两个参数
title = scrapy.Field(
# 当这个值传进来的时候,可以进行预处理,在方法中,可以添加处理多个函数,从左到右执行
input_processor = MapCompose()
)
create_date = scrapy.Field(
input_processor=MapCompose(date_convert),
# 因为是数组,所以取一个数据,自定义itemloader后就不用这样写了
output_processor = TakeFirst()
)
url = scrapy.Field()
# 文章链接进行MD5
url_object_id = scrapy.Field()
# 获取到封面图,这个字段必须是一个list,此时在msyql插入语句中,要获取到第一个值
front_image_url = scrapy.Field(
# 默认的覆盖掉
output_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)
)
content = scrapy.Field()
#因为tag本身是list,所以要重写
tags = scrapy.Field(
input_processor=MapCompose(remove_comment_tags),
output_processor=Join(',')
)
网友评论