初识scrapy
学习scrapy笔记
链接:https://pan.baidu.com/s/1NLpLEk2cv8QbssFV-PzUTQ
提取码:i0wv
srapy项目搭建
创建虚拟工作环境
mkvirtalenv <name>
workon <name>
安装scrapy
pip install -i https://pypi.douban.com/simple scrapy
创建项目
scrapy strartproject <projectname>
创建项目模板
scrapy genspider [options] <name> <domain>
运行项目
scrapy crawl <name>
终端调试
scrapy shell <domian>
parcharm环境设置
file
=>settings
=>Project Interpreter
=>loaction
选择当前项目目录=>base interpreter
选择运行环境目录=>ok
后会生成venv文件夹
parcharm调试
在项目主目录下添加main.py,debug此文件即可进入调试模式
from scrapy.cmdline import execute
import sys
import os
# 添加当前项目运行环境
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
execute(["scrapy", "crawl", <name>])
srapy选择器
数据爬下来干嘛,当然是筛选我们想要的咯。三大法宝css选择器、xpath选择器、正则
xpath
语法:
找个网页抓两个就会了,参考这篇文章
更简便的方案:打开chorme
=>F2
=>右键
=>copy
=>copy xpath
!
css
跟前端一样,继续看他!
scrapy Items
爬取的主要目标就是从非结构性的数据源提取结构性数据,例如网页。 Scrapy spider可以以python的dict来返回提取的数据.虽然dict很方便,并且用起来也熟悉,但是其缺少结构性,容易打错字段的名字或者返回不一致的数据,尤其在具有多个spider的大项目中。
Item使用简单的class定义语法以及 Field
对象来声明。例如:
import scrapy
class Product(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
last_updated = scrapy.Field(serializer=str)
scrapy 保存图片到本地
1)在settings.py中打开 ITEM_PIPELINES 的注释,在 ITEM_PIPELINES 中加入
ITEM_PIPELINES = {
'spider_first.pipelines.SpiderFirstPipeline': 300,
'scrapy.pipelines.images.ImagesPipeline':5, #后面的数字代表执行优先级 ,当执行pipeine的时候会按照数字由小到大执行
}
2)settings.py中加入
IMAGES_URLS_FIELD ="image_url" #image_url是在items.py中配置的网络爬取得图片地址
#配置保存本地的地址
project_dir=os.path.abspath(os.path.dirname(__file__)) #获取当前爬虫项目的绝对路径
IMAGES_STORE=os.path.join(project_dir,'images') #组装新的图片路径
还有很多设置有特殊需要的话可以用哦 (详情可以去imagepipeine源码查看)
IMAGES_MIN_HEIGHT=100 #设定下载图片的最小高度
IMAGES_MIN_WIDTH=100 #设定下载图片的最小宽度
........
3)可能报错
ModuleNotFoundError: No module named 'PIL'
安装pillow库即可
pip install https://pypi.douban.com/simple pillow
ValueError: Missing scheme in request url: h
字段必须是list,将字段改成list即可
4)获取图片保存本地的地址,则需要重写ImagesPipeline,并且在setting中调用重写的pipeline
from scrapy.pipelines.images import ImagesPipeline
class ArticleImagePipeline(ImagesPipeline):
# 重载ImagePipeline中的item_completed方法,获取下载地址
def item_completed(self, results, item, info):
for ok,value in results: #通过断点可以看到图片路径存在results内
image_file_path=value['path'] #将路径保存在item中返回
item['front_image_path']=image_file_path
return item
scrapy pipelines
当Item在Spider中被收集之后,它将会被传递到Item Pipeline,一些组件会按照一定的顺序执行对Item进行处理。
每个Item Pipeline都是实现了简单方法的Python类,比如决定此Item是丢弃而存储。以下是Item Pipeline的典型应用:
- 验证爬取的数据(检查爬取的数据是否包含某些字段,数据清洗)
- 查重(重复的数据丢弃)
- 将爬取的结果保存到文件或数据库
编写pipeline class
在pipelines.py文件中定义一个Pipeline类,同时必须实现下面的方法:
process_item(self,item,spider)
Pipeline 除此之外还可以实现以下方法:
open_spider(self, spider)
当spider被开启是调用该方法。
spider参数:被开启的spider对象
close_spider(self, spider)
当spider被关闭时,这个方法被调用
spider参数:被关闭的spider对象
在settings配置Item Pipeline
ITEM_PIPELINES = {
# 我们写好的Pipeline的路径,300表示优先级,范围(0-1000),越小级别越高
'scrapydemo.pipelines.BaiduPipeline': 300,
}
将数据插入mysql
安装mysqlclient
pip install -i https://pypi.douban.com/simple mysqlclient
同步插入
import MySQLdb
class MysqlPipeline(object):
# 该方法是同步的,容易发生堵塞
def __init__(self):
self.conn = MySQLdb.connect(host, user, password, db, charset='utf8',
use_unicode=True)
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
insert_sql = """
insert into jobble_article(title, create_date, url, url_object_id, font_img_url, praise_nums, comment_nums, fav_nums, tags) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
self.cursor.execute(insert_sql, (item['title'], item['create_date'], item['url'], item['url_object_id'],
item['font_img_url'], item['praise_nums'],
item['comment_nums'], item['fav_nums'], item['tags']))
self.conn.commit()
利用twisted
异步插入
from twisted.enterprise import adbapi
import MySQLdb.cursors
class MysqlTwistedPipeline(object):
@classmethod
def from_settings(cls, setting):
dbparams = 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,
)
dbpool = adbapi.ConnectionPool('MySQLdb', **dbparams)
return cls(dbpool)
def __init__(self, dbpool):
self.dbpool = dbpool
# 使用twisted将MYSQL插入变成异步执行
def process_item(self, item, scrapy):
query = self.dbpool.runInteraction(self.do_insert, item)
query.addErrback(self.handle_error, item, scrapy) # 处理异常
def handle_error(self, failure, item, scrapy):
print(failure)
def do_insert(self, cursor, item):
# 执行具体的操作
insert_sql = """
insert into jobble_article2(title, create_date, url, url_object_id, font_img_url, praise_nums, comment_nums, fav_nums, tags) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
cursor.execute(insert_sql, (item['title'], item['create_date'], item['url'], item['url_object_id'],
item['font_img_url'], item['praise_nums'],
item['comment_nums'], item['fav_nums'], item['tags']))
代码优化
scrapy提供了Itemloader,使代码看起来更加简洁和更具可配置性
from scrapy.loader import ItemLoader
item_loader = ArticleItemLoader(item=ItemLoader(), response=response)
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', '.entry-meta-hide-on-mobile::text')
item_loader.add_value('font_img_url', [font_img_url])
item_loader.add_css('praise_nums', '.vote-post-up h10::text')
item_loader.add_css('comment_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', '.entry')
article_item = item_loader.load_item()
yield article_item
此时,将数据处理逻辑放在Item中,scrapy.Field提供两个参数input_processor, output_processor
使用MapCompose处理输入参数
from scrapy.loader.processors import MapCompose
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 JobboleAriticleItem(scrapy.Item):
create_date = scrapy.Field(
input_processor=MapCompose(date_convert),
)
# MapCompose会从左到右处理
处理完成后参数是List,一般不是要这个的。重写ItemLoader
from scrapy.loader import ItemLoader
class ArticleItemLoader(ItemLoader):
default_output_processor = TakeFirst()
将上面实例化ItemLoader改成ArticleItemLoader
from ArticleSpider.items import ArticleItemLoader
item_loader = ArticleItemLoader(item=ItemLoader(), response=response)