一、创建scrapy项目
1.安装scrapy环境
pip install -i 镜像网站 scrapy
2.在指令目录创建scrapy工程
scrapy startproject 项目名
二、创建爬虫文件
Scrapy爬虫具有一下特性: Scrapy的方法,负责将爬取的页面数据包装成response
Scrapy方法会为start_url中的所有地址执行一遍
Scrapy具有网页追踪机制,当我们在parse提供一个yield的请求后将会不断请求传入的url,并执行回调函数进行解析
一.在spider目录下创建一个爬虫文件(这里scrapy项目会自动的从spiders文件夹下寻找爬虫文件)---jobbole.py
二.在爬虫文件下创建类,继承scrapy.spider
1.指定name,指定爬虫程序名称,在启动scrapy时需要用到
2.指定allowed_domains(非必要),指定爬去url的域名,如果不在该域名下的url将被过滤不去爬取
3.start_urls爬取的起始url
4.一定要把ROBOTSTXT_OBEY 设置成false,否则会查询每一个网站是否同意robots协议。如果网站设置的false,那么将不爬去
5.parse函数负责解析数据,在这里负责解析,在最后调用yield返回Request对象,Request的callback执行parse,这样就可以不断反复爬取。
三。定义一个Item类,继承自scrapy.item,负责保存数据
四。在piplines文件下创建一个piplines。提供process_item方法,负责接收parse传递过来的item
五。在settings中的ITEM_PIPLELINES字典中指定启用的pipelines。
在爬虫类的parse方法中,如果使用yield返回了item,那数据将交给piplines处理,piplines这个类负责保存数据。
1。保存Json数据的pipline
使用scrapy提供的export即可(scrapy提供了多种export)
from scrapy.exporters import JsonItemExporter
class JobJsonExporter(object):
def __init__(self):
self.file = codecs.open('job_exporter.json', 'wb')
self.exporter = JsonItemExporter(file=self.file, encoding='utf-8', ensure_ascii=False)
self.exporter.start_exporting()
def process_item(self, item, spider):
# 传递数据给exporter
self.exporter.export_item(item)
return item
def spider_closed(self, spider):
self.exporter.finish_exporting()
self.file.close()
2.保存数据到数据库
scrapy提供了twisted类来完成异步数据库的读写操作,需要引入twisted和pymysql
from twisted.enterprise import adbapi
import pymysql.cursors
class JobMySqlPipline(object):
def __init__(self,dbPool):
self.dbPool=dbPool
##这个方法会在pipline初始化的时候调用
@classmethod
def from_settings(cls, settings):
host = settings["MY_HOST"]
port = settings["MY_PORT"]
account = settings["MY_ACCOUNT"]
password = settings["MY_PASSWORD"]
database = settings["MY_DATABASE"]
# 要跟使用的mysql框架对应
dbParams = dict(host=host, db=database, user=account, password=password, port=port,
charset='utf8', use_unicode=True)
print("from settings : " + host + " password : " + password)
#将数据库操作编程异步操作,这里pymysql代表使用的数据库
dbPool = adbapi.ConnectionPool("pymysql", **dbParams)
return cls(dbPool)
def process_item(self, item, spider):
# 传递数据给exporter
# 使用twisted和mysql将数据库插入编程异步操作,需要指定插入方法回调
query= self.dbPool.runInteraction(self.do_insert,item)
#添加异步错误处理操作
query.addErrback(self.handleError,item,spider)
return item
#执行插入方法回调
def do_insert(self,cursor,item):
sql = """
insert into article_spider(title,url,front_img_path,praise_nums)
VALUES (%s,%s,%s,%s)
"""
cursor.execute(sql, (item['title'], item['url'], item['front_img_url'], item['praise_nums'],))
#不再需要commit操作,这个操作自动完成
#self.conn.commit()
###处理异常毁掉
def handleError(self,failuer,item,spider):
print(failuer)
三、调试scrapy
创建一个入口函数文件,如上图的main.py.
1.使用sys.path.append方法将main.py加入到环境变量中
2.使用execute函数,执行脚本,这里第三个参数代表执行的爬虫文件,一定要和爬虫文件中的Name对应上
import sys
import os
from scrapy.cmdline import execute
##output:/Users/apple/Desktop/workspace/pythonwork/ArticleSpider
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
execute(['scrapy',"crawl","jobbole"])
四、Item优化
使用Itemloader来封装数据,使用ItemLoader封装数据后,赋予的每一个字段值都是数组
1.引入Itemloader
from scrapy.loader import ItemLoader
2.使用add方法解析数据
创建ItemLoader对象,传入reponse和item类型对象
add_css 负责解析css谁
add_xpath 解析xpath数据
add_value 直接对字赋值
load_item:生成item
itemLoader=ItemLoader(item=JoBBoleArticleItem(),response=response)
itemLoader.add_css("title",".entry-header h1::text")
itemLoader.add_css("create_date","p.entry-meta-hide-on-mobile::text")
itemLoader.add_value("front_img_url",[front_image_url])
itemLoader.add_value("url",response.url)
itemLoader.add_css("praise_nums",".vote-post-up h10::text")
itemLoader.add_css("commment_nums","a[href='#article-comment'] span::text")
#解析生成item
article_item=itemLoader.load_item()
获取到的值:title,url等拿到的都是一个数组
2.通过processor预处理拿到的数据
引入MapCompse这个processor,可以对字段执行一些函数
from scrapy.loader.processors import MapCompose,TakeFirst
通过scrapy.Field方法指定input_processor为MapCompose,这里表示在输入预处理函数为MapCompose,而MapCompose又接收多个函数,表示拿到的数据依次执行传入的方法(例如title,依次执行了匿名函数,addTitle方法)
def addTtitle(value):
return value+'-guo'
"""
解析时间
"""
def parseData(data):
try:
create_date = datetime.datetime.strptime(data, "%Y/%m/%d").date()
except Exception as e:
create_date = datetime.datetime.now().date()
return create_date
class JoBBoleArticleItem(scrapy.Item):
"""
标题
"""
title = scrapy.Field(
#MapCompose需要传入一个回调函数,可以对取到的值做预处理
#可以接收多个方法
input_processor=MapCompose(lambda x:x+"-spider",addTtitle)
)
create_date = scrapy.Field(input_processor=MapCompose(parseData))
3.解决字段值为数组问题
1.通过引入TakeFirst这个Processors,它可以自动的取出数组中第一个值作为字段值。
2.自定义ItemLoader
3.指定Item的默认输出类型为TakeFirst
class FirstItemLoader(ItemLoader):
default_output_processor = TakeFirst()
4.使用自定义ItemLoader替代ItemLoader
itemLoader=FirstItemLoader(item=JoBBoleArticleItem(),response=response)
itemLoader.add_css("title",".entry-header h1::text")