python--Scrapy模块的使用
python--Scrapy模块的使用
Scrapy引擎:
用来接收引擎发送过来的请求,压入队列中,并在引擎再次请求时返回,就是在我们所要爬取的url全部放入一个优先队列中,由它来决定下一个处理的url是什么,同时他会自动将重复的url去除
注意:我们在创建一个项目时,在spider中会存在一个start_urls = ['http://dig.chouti.com/'],他将是我们的初始url,会在项目启动后被引擎放入调度器中开始处理
用于下载网页内容,并将网页内容返回给蜘蛛(下载器是基于twisted实现)
Item解析器:
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
Downloader Middlewares下载器中间件
介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
Scheduler Middewares调度中间件
1.引擎从调度器中取出一个连接URL,用于接下来的抓取
3.下载器将资源下载,封装为应答包Response
5.解析出实体Item,将实体通过管道解析持久化操作
注意:第一步之前,是先去爬虫start_url中获取初始网址,进行操作
1.创建项目
cd 项目名
scrapy genspider 项目列表名 初始url(后面可以修改)
cd scrapyPro
scrapy genspider chouti chouti.com
3.展示爬虫应用列表
scrapy crawl 爬虫应用名称
scrapy crawl chouti --nolog #--nolog不打印日志
项目结构
1.创建项目
cd 项目名
scrapy genspider 项目列表名 初始url(后面可以修改)
cd scrapyPro
scrapy genspider chouti chouti.com
3.展示爬虫应用列表
scrapy crawl 爬虫应用名称
scrapy crawl chouti --nolog #--nolog不打印日志
项目结构
chouti爬虫的编写
# -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # https://doc.scrapy.org/en/latest/topics/items.html import scrapy class ScrapyproItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() pass class ChoutiItem(scrapy.Item): # define the fields for your item here like: title = scrapy.Field() href = scrapy.Field()
pipeline.py文件编写:实现数据持久化操作
ITEM_PIPELINES = { 'scrapyPro.pipelines.ScrapyproPipeline': 300, #后面300代表优先级 }
补充:Selector的操作
Selector(response=response).xpath('/html/body/ul/li/a/@href').extract() #提取所有的,是个列表
Selector(response=response).xpath('//body/ul/li/a/@href').extract_first() #提取第一个
注意:'//'代表子孙标签,'/'代表子标签,另外'./'代表当前标签下寻找
import scrapy,hashlib from scrapy.selector import Selector,HtmlXPathSelector from scrapy.http import Request class ChoutiSpider(scrapy.Spider): name = 'chouti' allowed_domains = ['chouti.com'] start_urls = ['http://dig.chouti.com/'] visited_urls = set() #用于存放我们获取的url的md5值,而且是集合去重 def md5(self,url): #将url转md5 ha_obj = hashlib.md5() ha_obj.update(bytes(url,encoding="utf-8")) key = ha_obj.hexdigest() return key def parse(self, response): page_objs = Selector(response=response).xpath("//div[@id='dig_lcpage']//a[@class='ct_pagepa']") #解析实体 for page in page_objs: href = page.xpath("@href").extract_first() ha_href = self.md5(href) if ha_href in self.visited_urls: pass else: self.visited_urls.add(ha_href) #将获取的url添加到集合 new_url = "https://dig.chouti.com%s"%href
yield Request(url=new_url,callback=self.parse) #请求新的url,获取下面的url 对于阻塞操作使用yield切换,实现异步
注意:默认是获取所有的页面100多页,我们可以在setting文件设置解析深度
简单实例应用:获取校花网的图片和姓名,按照姓名进行持久化文件归类
# -*- coding: utf-8 -*-
import scrapy
import hashlib
from scrapy.http.request import Request
from scrapy.selector import Selector,HtmlXPathSelector
import scrapy.http.response.html
from ..items import XiaohuaItem
class XiaohuaSpider(scrapy.Spider):
name = 'xiaohua'
allowed_domains = ['xiaohua.com']
start_urls = ['http://www.xiaohuar.com/']
visited_url = set()
visited_url_img = {}
visited_url_title = {}
def md5(self,url):
hash_obj = hashlib.md5()
hash_obj.update(bytes(url,encoding="utf-8"))
return hash_obj.hexdigest()
def parse(self, response):
#获取首页中所有的人物的下一级url,过滤掉校草
xh_a = Selector(response=response).xpath("//ul[@class='twoline']/li")
for xh in xh_a:
a_url = xh.xpath("./a[@class='xhpic']/@href").extract_first()
a_title = xh.xpath("./a/span/text()").extract_first()
if a_title.find("校草") != -1:
continue
if not a_url.startswith("http"):
a_url = "http://www.xiaohuar.com%s" % a_url
ha_url = self.md5(a_url)
if ha_url in self.visited_url:
pass
else:
self.visited_url.add(ha_url)
self.visited_url_title[ha_url]=a_title
yield scrapy.Request(url=a_url,callback=self.parse,dont_filter=True)
#下面是所有人物下一级url中去查找所有照片,注意:部分小照片和大照片的区别在于前面多了small,大照片只取前32位即可
#/d/file/20171202/small062adbed4692d28b77a45e269d8f19031512203361.jpg 小照片
#/d/file/20171202/ 062adbed4692d28b77a45e269d8f1903.jpg 大照片
xh_img = Selector(response=response).xpath("//div[@class='post_entry']")
xh_img_a = xh_img.xpath("./ul//img/@src")
if len(xh_img) == 0:
xh_img = Selector(response=response).xpath("//div[@class='photo-Middle']/div")
if len(xh_img) != 0:
xh_img = xh_img[1]
else:
xh_img = Selector(response=response).xpath("//div[@class='photo-m']/div")[1]
xh_img_a = xh_img.xpath("./table//img/@src")
# 上面找到标签,下面开始对标签进行循环,获取所有照片url
for xh_img_item in xh_img_a:
xh_img_url = xh_img_item.extract()
if xh_img_url.find("small"):
tmp_list = xh_img_url.rsplit("/",1)
tmp_name_list = tmp_list[1].replace("small","").split(".")
xh_img_url = "/".join([tmp_list[0],".".join([tmp_name_list[0][:32],tmp_name_list[1]])])
if not xh_img_url.startswith("http"):
xh_img_url = "http://www.xiaohuar.com%s" % xh_img_url
#将所有照片加入字典 url:名字
self.visited_url_img[xh_img_url] = self.visited_url_title[self.md5(response.url)]
#若是收集完成,那么两者的长度是一致的,开始进行持久化
if len(set(self.visited_url_img.values())) == len(self.visited_url) and len(self.visited_url) != 0:
for xh_url in self.visited_url_img.items():
item_obj = XiaohuaItem(title=xh_url[1],img_url=xh_url[0])
yield item_obj
items.py
import requests,os
class XiaohuaPipeline(object):
def process_item(self, item, spider):
'''
title
img_url
'''
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),'upload',item['title'])
if not os.path.isdir(file_path):
os.makedirs(file_path)
response = requests.get(item['img_url'],stream=False)
with open(os.path.join(file_path,item['img_url'].rsplit("/",1)[1]),"wb") as fp:
fp.write(response.content)
return item
settings.py
原因:可能是我在设置allowed_domains允许域名中所设置的域名不是我们所爬取的网站域名
因为在网站中可能有外联,我们只是需要去访问该网站,而不是他的外联网站,所以设置allowed_domains是必须的,可以过滤掉外联的网站,要是希望获取外联网站,我们在该列表中添加上即可。
# -*- coding: utf-8 -*- import scrapy import hashlib from scrapy.http.request import Request from scrapy.selector import Selector,HtmlXPathSelector import scrapy.http.response.html from ..items import XiaohuaItem class XiaohuaSpider(scrapy.Spider): name = 'xiaohua' allowed_domains = ['xiaohua.com'] start_urls = ['http://www.xiaohuar.com/'] visited_url = set() visited_url_img = {} visited_url_title = {} def md5(self,url): hash_obj = hashlib.md5() hash_obj.update(bytes(url,encoding="utf-8")) return hash_obj.hexdigest() def parse(self, response): #获取首页中所有的人物的下一级url,过滤掉校草 xh_a = Selector(response=response).xpath("//ul[@class='twoline']/li") for xh in xh_a: a_url = xh.xpath("./a[@class='xhpic']/@href").extract_first() a_title = xh.xpath("./a/span/text()").extract_first() if a_title.find("校草") != -1: continue if not a_url.startswith("http"): a_url = "http://www.xiaohuar.com%s" % a_url ha_url = self.md5(a_url) if ha_url in self.visited_url: pass else: self.visited_url.add(ha_url) self.visited_url_title[ha_url]=a_title yield scrapy.Request(url=a_url,callback=self.parse,dont_filter=True) #下面是所有人物下一级url中去查找所有照片,注意:部分小照片和大照片的区别在于前面多了small,大照片只取前32位即可 #/d/file/20171202/small062adbed4692d28b77a45e269d8f19031512203361.jpg 小照片 #/d/file/20171202/ 062adbed4692d28b77a45e269d8f1903.jpg 大照片 xh_img = Selector(response=response).xpath("//div[@class='post_entry']") xh_img_a = xh_img.xpath("./ul//img/@src") if len(xh_img) == 0: xh_img = Selector(response=response).xpath("//div[@class='photo-Middle']/div") if len(xh_img) != 0: xh_img = xh_img[1] else: xh_img = Selector(response=response).xpath("//div[@class='photo-m']/div")[1] xh_img_a = xh_img.xpath("./table//img/@src") # 上面找到标签,下面开始对标签进行循环,获取所有照片url for xh_img_item in xh_img_a: xh_img_url = xh_img_item.extract() if xh_img_url.find("small"): tmp_list = xh_img_url.rsplit("/",1) tmp_name_list = tmp_list[1].replace("small","").split(".") xh_img_url = "/".join([tmp_list[0],".".join([tmp_name_list[0][:32],tmp_name_list[1]])]) if not xh_img_url.startswith("http"): xh_img_url = "http://www.xiaohuar.com%s" % xh_img_url #将所有照片加入字典 url:名字 self.visited_url_img[xh_img_url] = self.visited_url_title[self.md5(response.url)] #若是收集完成,那么两者的长度是一致的,开始进行持久化 if len(set(self.visited_url_img.values())) == len(self.visited_url) and len(self.visited_url) != 0: for xh_url in self.visited_url_img.items(): item_obj = XiaohuaItem(title=xh_url[1],img_url=xh_url[0]) yield item_obj
items.py
import requests,os
class XiaohuaPipeline(object): def process_item(self, item, spider): ''' title img_url ''' file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),'upload',item['title']) if not os.path.isdir(file_path): os.makedirs(file_path) response = requests.get(item['img_url'],stream=False) with open(os.path.join(file_path,item['img_url'].rsplit("/",1)[1]),"wb") as fp: fp.write(response.content) return item