杂乱无章的scrapy细碎知识
1.
def parse(self, response): #获取响应头 print(response.headers) #获取状态码 print(response.status) #获取响应的二进制数据 print(response.body) #获取响应的字符串数据 print(response.text)
2. Scrapy优势:用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容及图片。非常方便,另外它使用了Twisted这个异步网络框架来处理网络通讯,不用我们自己实现异步框架,除此之外,它还包含了各种中间件接口,可以灵活的实现各种需求。
3. 图片下载setting配置 :
#当项目需要启动pipeline的时候,不管是自定义的pipeline或者是scrapy自带的pipeline都需要在ITEM_PIPELINES里面进行配置 ITEM_PIPELINES = { # 'ImageSpider.pipelines.ImagespiderPipeline': 150, # 'ImageSpider.pipelines.FilespiderPipeline':200, #数字表示是通过pipeline管道的优先级,数字越小,优先级越高 'scrapy.pipelines.images.ImagesPipeline': 1 } #IMAGES_STORE是在设置图片的存储路径,也可以设置为绝对路径 IMAGES_STORE="imgs" #IMAGES_URLS_FIELD是在配置爬虫根据item对象中的哪一个字段进行url的请求,对图片进行下载 IMAGES_URLS_FIELD='src' #图像管道避免下载最近已经下载的图片置IMAGES_EXPIRES可以调整失效期限,可以用天数来指定: IMAGES_EXPIRES=7 #图片管道可以自动创建下载图片的缩略图。 IMAGES_THUMBS={ 'small':(30,30), 'big':(100,100) } # # 如果只想要宽度大于250的图片,只需IMAGES_MIN_HEIGHT 和 IMAGES_MIN_WIDTH 设置中指定最小允许的尺寸。 IMAGES_MIN_HEIGHT = 0 IMAGES_MIN_WIDTH = 250
# # 生成后的目录结构如下
- imgs
-full #原图
-thumbs #设定好大小的略缩图
4. items.py中的设置
import scrapy class NovelspiderItem(scrapy.Item): #NovelspiderItem这个item类继续成scrapy.Item这个类,NovelspiderItem这个类的主要作用就声明一个字典类型的对象,可以实现类似于字典对象的存储和读取。 #novel_name,novel_info,novel_type,novel_author这些相当于是字典对象中的键。 novel_name=scrapy.Field() novel_info=scrapy.Field() novel_type=scrapy.Field() novel_author=scrapy.Field()
5. 通过命令储存item字段
#scrapy自带的命令可以将yield item返回的item对象生成Json文件保存到本地进行存储。保存的时候,网页解析的数据是Unicode编码,需要转出成utf-8编码之后再进行存储 #命令:scrapy crawl novel -o 文件名.json -s FEED_EXPORT_ENCODING=utf-8 #命令:scrapy crawl novel -o novel1.csv scrapy自带的存储为csv文件的api #命令:scrapy crawl novel -o novel.xml scrapy自带的存储为xml文件的api #命令:scrapy crawl novel -o novel.jsonlines 将存储的json文件中的每一个item都输出一行 #使用scrapy自带的scrapy crawl novel -o novel.csv 将数据存为csv文件时,发现文件有空行的解决方法: #找到scrapy/exporters.py文件,找到CsvItemExporter类,在io.TextIOWrapper函数的参数里增加参数 newline=''
6. pipelines.py自定义
- 文件下载
- 通过访问url直接获得下载文件时(比如小说网站的下载按钮,或者图片),可以使用scrapy内置的pipelines; 这时,不用再pipelines.py中写逻辑,只需要在settings.py中配置就好
-
ITEM_PIPELINES = { # 'QiShuSpider.pipelines.QishuspiderPipeline': 300, #scrapy自带的图片下载pipeline 'scrapy.pipelines.images.ImagesPipeline': 1, # scrapy自带的文件下载pipeline 'scrapy.pipelines.files.FilesPipeline': 2 } #设置图片的存储位置 IMAGES_STORE='imgs' IMAGES_URLS_FIELD='novel_img_url' #设置文件的存储位置 FILES_STORE='files' FILES_URLS_FIELD='novel_download_url'
#目录结构如3
- 自定义图片下载, 需要重写ImagesPipeline中的方法
- 这个类继承了ImagesPipeline, 重写了其中的两个方法
- 在get_media_requests方法中设定请求图片的url(替代了在settings.py中的IMAGES_URLS_FIELD='itemurl字段'这一步)
- 在file_path方法中设置图片下载的 路径 以及 名称
- 重写ImagesPipeline的类:
import scrapy from scrapy.pipelines.images import ImagesPipeline # # class ZhanzhangsucaispiderPipeline(object): # def process_item(self, item, spider): # return item #自定义图片存储pipeline,是基于Scrapy自带的ImagesPipeline实现的,只需要在ImagesPipeline的基础上,重写图片的保存路径和图片的名称相对应的方法。 class CustomImagePipeline(ImagesPipeline): #重写Scrapy自带的ImagesPipeline中get_media_requests这个方法的主要目的是,是为了通过Request对象给file_path传递一个item,这个item里面包含着图片分类的名称和图片的url地址 def get_media_requests(self, item, info): for image_url in item['download_url']: yield scrapy.Request(image_url,meta={"item":item}) #Scrapy自带的ImagesPipeline中实现图片存储路径和图片名称的关键方法就是file_path,这也就代表着想要自定义图片存储路径和图片名称,就可以考虑重写ImagesPipeline中的file_path这个方法,就可以达到想要的结果。 def file_path(self, request, response=None, info=None): #file_path这个函数的参数request接收到的就是传过来的Request对象,只需要通过request调用meta属性即可拿到Request对象传递过来的item对象,再通过解析item对象,即可获取相对应的图标分类名称和每张图标图片对应的地址。再通过图标分类名称和图标图片的地址创建文件存储路径即可。 ''' 用于自定义图片的存储文件夹和图片的名称 :return: 路径 ''' item=request.meta["item"] #从item对象当中取出图片的url,取出每个小分类的名称 title=item["title"] image_name=item["download_url"][0].split("/")[-1] path="%s/%s"%(title,image_name) return path
- 自定义pipeline用各种方式(数据库,json,csv等)存储item
- 重点有三个方法,一个__init__方法,一个有装饰器的方法
- 三个方法分别是open_spider, process_item, close_spider ,其中open_spider方法在爬虫开始时启动,close_spider结束时启动,前者可以写开启某个数据库,csv等文件,后者则关闭相关数据库,csv等文件. process_item是重写pipeline必须实现的一个方法,里面实现了对item的处理方式,但是返回值仍然是item,只是对item做了处理,使爬虫在传递item的时候同时使用我们设定的方法存储数据.
- __init__方法中初始化类变量,from_crawler方法主要功能是从settings.py中读取相关配置
-
import json import codecs import csv #自定义的每一个itempipeline必须是一个独立的Python类,也就是继承于object class NovelspiderPipeline(object): def __init__(self): #初始化函数只执行一次 self.f=codecs.open(filename="novel.json",mode="w",encoding="utf-8") self.f.write("[") print("初始化函数执行了") def open_spider(self,spider): #open_spider只执行一次 # self.f = codecs.open(filename="novel.json", mode="w", encoding="utf-8") #当爬虫开启的时候,这个函数会被调用 print("%s-爬虫启动了"%spider.name) def process_item(self, item, spider): ''' process_item这个方法是必须要实现的,也就是说想要自定义一个itempipeline,这个方法是必须要使用的。而且从spider中yield过来的item都必须要经过process_item这个函数来处理,每传递过来一个item这个函数就会被执行一次。 :param item: 每一个从spider中yield过来的item。 :param spider: 当前的爬虫(指代的是哪一个爬虫) :return: 当process_item每处理完一个item,最后都要将这个item返回出去,因为后续可能还有其他的函数需要处理这个item。 ''' print("process_item执行了") #由于item并不真正的字典,它是一个NovelSpiderItem类型的数据,所有需要进行转换 item_dict=dict(item) #因为json.dumps在序列化的时候回对中文默认使用ascii编码,想要真正输出中文需要设置ensure_ascii=False json_str=json.dumps(item_dict,ensure_ascii=False) self.f.write(json_str+",") return item def close_spider(self,spider): #close_spider只执行一次 self.f.seek(self.f.tell()-1) self.f.write("]") self.f.close() #当爬虫结束时,该函数会被调用 print("%s-爬虫关闭了"%spider.name) # @classmethod # def from_crawler(cls,crawler): # #主要功能是从settings文件中读取相关配置的。 # pass class NovelSpiderCSVPipeline(object): def open_spider(self,spider): self.f=open("novel.csv","w",encoding="utf-8",newline="") self.writer=csv.DictWriter(self.f,fieldnames=["novel_name","novel_info","novel_type","novel_author"]) self.writer.writeheader() def process_item(self,item,spider): item_dict=dict(item) self.writer.writerow(item_dict) return item def close_spider(self,spider): self.f.close()
7. pipelines存储在mysql中
from jobspider.items import * import pymysql class JobspiderPipeline(object): def __init__(self): # 1. 建立数据库的连接 self.connect = pymysql.connect( host='localhost', port=3306, user='', passwd='', db='', charset='utf8' ) # 2. 创建一个游标cursor, 是用来操作表。 self.cursor = self.connect.cursor() def process_item(self, item, spider): if isinstance(item, ZhiHuItem): pass elif isinstance(item, JobspiderItem): # 3. 将Item数据放入数据库,默认是同步写入。 # INSERT INTO job(zwmc); DELETE DATABASE job; VALUES ('123') SQL注入 insert_sql = "INSERT INTO job(zwmc, zwxz, zpyq, gwyq) VALUES ('%s', '%s', '%s', '%s')" % (item['zwmc'], item['zwxz'], item['zpyq'], item['gwyq']) self.cursor.execute(insert_sql) # 4. 提交操作 self.connect.commit() def close_spider(self, spider): self.cursor.close() self.connect.close()
8. re_first
# re_first(): 直接将css返回列表中的第一个元素的数字提取出来 total_page_num = int(response.css('.td::text').re_first('共(\d+)页'))
9.