什么是框架:
通用性性强的项目半成品
什么是scrapy?
Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架。
Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。
Scrapy有自己的数据提取机制。它们被称为选择器,因为它们“选择”HTML文档的某些部分 XPath 或 CSS 表达。
XPath 是一种在XML文档中选择节点的语言,也可以与HTML一起使用。 CSS 是用于将样式应用于HTML文档的语言。它定义选择器,将这些样式与特定的HTML元素相关联。
通常我们可以很简单的通过 Scrapy 框架实现一个爬虫,抓取指定网站的内容或图片
环境安装:
-mac or linux :pip install scrapy
-window系统:
- pip install wheel
- 下载twisted,下载地址为:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twited
- 安装twisted:pip install Twisted-20.3.0-cp39-cp39-win_amd64.whl
- pip install pywin32 (pywin32提供了很多windows的操作接口)
- pip install scrapy
#测试在终端里录入scrapy指令,没有报错表示安装成功
工程步骤:
- 创建scrapy工程
终端输入(cmd): scrapy startproject scrapy_demo(工程名)
2.创建爬虫文件(爬取内容)mydomin 为spider文件名
进入spider文件中:scrapy genspider mydomain www.mydomain.com
3.运行爬虫文件
scrapy crawl spiderName
scrapy crawl spiderName --nolog :工程运行时去除日志(可以配置去掉)
python正则re:必须会 https://www.runoob.com/python/python-reg-expressions.html
项目工程文件分布:
- scrapy.cfg # 部署配置文件
- tutorial/ #project的Python模块,您将从这里导入代码__初始值
- items.py #项目项定义文件,定义爬取的名称来存入爬取到的值
- middleware.py #项目中间件文件,
- pipelines.py #项目管道文件
- settings.py #项目设置文件
- spider/ #爬虫目录,爬虫.py文件进入该目录下创建
spider文件夹:
########### scrapy genspider first_spider www.xxx.com
爬虫文件:
import scrapy
from shici_new.items import ShiciNewItem
class FirstSpiderSpider(scrapy.Spider):
#爬虫文件名称:爬虫源文件的一个唯一标识
name = 'first_spider'
#允许访问的域名(通常注释掉)
#allowed_domains = ['www.xxx.com']
#起始的url
start_urls = ['https://www.shicimingju.com/book/xiyouji.html']
def parse(self, response):
print("================"+response.url+"=======================")
list_ul= response.xpath('//div[@class="book-mulu"]/ul/li')
#获取拼接url
start_ur="https://www.shicimingju.com"
for li in list_ul:
#获取url
li_url=li.xpath("./a/@href").extract()[0]
title_url = start_ur+li_url
#获取title
title_new = li.xpath("./a/text()").get()
print(title_new+"========================")
title_new.strip()
item = ShiciNewItem()
item["title_new"] = title_new
#将item提交给context_parse方法获取内容
yield scrapy.Request(url=title_url,callback=self.content_parse,meta={"item": item})
#title url的数据获取并解析
def content_parse(self,response):
item = response.meta["item"]
# print(item["title_new"]) 成功获取item
title_content =response.xpath("//div[@class='chapter_content']//text()").extract()
title_content="".join(title_content).strip()
item["title_content"]=title_content
yield item
常用语法:
scrapy自带有xpath解析
例如:
li_url=li.xpath("./a/@href").extract()[0]
解析出来的list_url 类似于:用标签selector标签包裹,其结果类似于列表对象class:scrapy.selector.SelectorList,它标识一个列表
[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]
因此要获取标签中的值:
extract():获取结果,存入list中,如果是一个值可用【0】/.extract_first()
getall()和get()方法
re()提取:
response.css('title::text').re(r'Quotes.*')
['Quotes to Scrape']
response.css('title::text').re(r'Q\w+')
['Quotes']
response.css('title::text').re(r'(\w+) to (\w+)')
['Quotes', 'Scrape']
可以将默认返回值作为参数提供,以代替 None
:
>>> response.xpath('//div[@id="not-exists"]/text()').get(default='not-found')
'not-found'
而不是使用例如 '@src'
xpath可以使用 .attrib
A的性质 Selector
:
>>> [img.attrib['src'] for img in response.css('img')]
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
作为捷径, .attrib
也可以直接在SelectorList上使用;它返回第一个匹配元素的属性:
>>> response.css('img').attrib['src']
'image1_thumb.jpg'
当只需要一个结果时(例如,按id选择,或在网页上选择唯一元素时),此选项最有用:
response.css('base').attrib['href']
'http://example.com/'
-
Css选择器
-
要选择文本节点,请使用
::text
-
要选择属性值,请使用
::attr(name)
在哪里? name 是要为其值的属性的名称
将选择器与正则表达式一起使用
Selector
也有 .re()
使用正则表达式提取数据的方法。但是,与使用不同 .xpath()
或 .css()
方法, .re()
返回字符串列表。
下面是一个用于从 HTML code 以上:
>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
['My image 1',
'My image 2',
'My image 3',
'My image 4',
'My image 5']
def re(self, regex, replace_entities=True):
"""
``regex``可以是已编译的正则表达式,也可以是将使用
``re.compile(regex)``编译为正则表达式。默认情况下,
字符实体引用将替换为其对应字符(除`&;``外)和`<;``)
。将“替换”实体传递为“假”将关闭这些实体替代品。
"""
return extract_regex(regex, self.get(), replace_entities=replace_entities)
、、、re(regex, replace_entities=True)
'''默认情况下,字符实体引用替换为其相应的字符(除了 & 和 < )经过
replace_entities 作为 False 关闭这些替换。'''
yield scrapy.Request():
yield scrapy.Request(url=title_url,callback=self.content_parse,meta={"item": item})
*****把新地址传回搜索引擎,然后找到对应的方法来处理该请求,如果要传递值则用meta进行传递。item=response.meta["item"] 来获取。
item.py:
item对象结构的定义在项目录下的items.py文件中定义。以类的方式定义item对象的各个字段,这个类继承与scrapy.Item
class MyItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
url = scrapy.Field()
title = scrapy.Field()
item = MyItem()
item['url'] = 'http://www.xxx.com'
item['title'] = '类目'
Field对象
可以看到,在声明Item时,声明字段使用的是Field对象。这个Field对象其实完全继承自Python的字典,并且没有做任何改动,所以在使用Field声明字段时,可以传入数据作为这个字段的元数据(metadata),上方的serializer=str其实就是一个指定序列化函数的元数据。
字段的元数据与字段的值之间没有必然的联系。如果我们直接查看Item对象,那么获取的是字段的值,如果使用.fields
,获取的就是字段的元数据了
pipelines.py
>>项目管道的典型用途是:
-
清理 HTML 数据
-
验证抓取的数据(检查项目是否包含某些字段)
-
检查重复项(并删除它们)
-
将抓取的项目存储在数据库中
此外,它们还可以实现以下方法:
》》open_spider
(self,spider)这个方法在蜘蛛打开时被调用。
参数 :spider ( Spider
object) – openspider
》》close_spider(self,spider)当蜘蛛关闭时调用此方法。
》》from_crawler
( cls ,crawl)
具体:https://blog.youkuaiyun.com/xiaoyu_wu/article/details/102551523
如果存在,则调用此类方法以从Crawler
. 它必须返回管道的新实例。Crawler 对象提供对所有 Scrapy 核心组件的访问,如设置和信号;这是管道访问它们并将其功能挂钩到 Scrapy 的一种方式。
参数crawler ( Crawler
object) – 使用这个管道的爬虫
from itemadapter import ItemAdapter
#保存文档
class ShiciNewPipeline:
fp = None
def open_spider(self,spider):
print("开始爬虫。。。")
self.fp = open("./西游记title.txt","w",encoding="utf-8")
def process_item(self, item, spider):
title_xiyou = item["title_new"]
title_content = item["title_content"]
self.fp.write(title_xiyou+"\n"+title_content+"\n")
print(title_xiyou+"文档保存成功")
return item
def close_spider(self,spider):
print("结束爬虫")
self.fp.close()
import pymysql
#保存到mysql
class MysqlPipeline:
conn = None
cursor = None
def open_spider(self,spider):
print("创建mysql驱动")
self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='root',db='myspider',charset='utf8')
print("创建成功")
def process_item(self,item,spider):
#获取sql游标
self.cursor = self.conn.cursor()
sql ="insert into myshici (title) values (%s);"
try:
tit = item["title_new"]
tit.strip()
self.cursor.execute(sql,tit)
self.conn.commit()
except Exception as e:
print(e)
print("失败")
self.conn.rollback()
return item
def close_spider(self,spider):
self.cursor.close()
self.conn.close()
pipelines.py文件中可以有多个管道类:
同时保存在多个平台上,比如本地,mysql,radis等
***********注意*****
1.不要忘记在settings配置文件中添加配置(值越小优先级越高)
ITEM_PIPELINES = {
'shici_new.pipelines.ShiciNewPipeline': 300,
'shici_new.pipelines.MysqlPipeline' : 305,
}
2.要return item ,如果有多个管道类则可以获取到item值进行处理储存,如果不返回,优先级低的管道类获取不到item
图片管道:
1. 要继承scrapy中管道里的图片管道
2. 要在setting配置中配置图片存储的路径
IMAGES_STORE='/imgs' :表示最终图片存储路径
3.开启管道配置
#image管道类
from scrapy.pipelines.images import ImagesPipeline
import scrapy
class ImagePipline(ImagesPipeline):
#将图片地址返回到请求,图片地址进行请求操作
def get_media_requests(self, item, info):
yield scrapy.request(item['url'])
#返回图片存储的路径和名字
def file_path(self, request, response=None, info=None, *, item=None):
file_name=request.url.split('/')[-1]
return file_name
#将item返回到下一个执行的管道
def item_completed(self, results, item, info):
return item
中间件:middlewares.py
下载器中间件是位于引擎和下载器之间的特定钩子,当它们从引擎传递到下载器时处理请求,以及从下载器传递到引擎的响应。
如果需要执行以下操作之一,请使用下载器中间件:
-
在将请求发送给下载者之前处理该请求(即在Scrapy将请求发送到网站之前);
-
变更在传递给spider之前收到响应;
-
发送新的请求,而不是将收到的响应传递给spider;
-
在不获取网页的情况下将响应传递给蜘蛛;
-
悄悄地放弃一些请求。
主要作用:
- 在Scrapy将请求发送到网站之前修改,处理请求,如:更换代理ip,header等
- 在将响应传递给引擎之前处理收到的响应,如:响应失败重新请求,或将失败的做一定处理再返回给引擎
- 忽略一些响应或者请求
默认下载中间件
scrapy内置了一些默认配置,这些是不允许被修改的,通常是_BASE结尾的设置,比如DOWNLOADER_MIDDLEWARES_BASE下载中间件的默认设置,如下
{
'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}
scrapy就是按照上面数字从小到大依次执行的,比如执行完RobotsTxtMiddleware的process_request()方法后会继续执行下面HttpAuthMiddleware等process_request(),可以看作串联的形式依次过流水线
如果我们要添加自定义的下载中间件,需要在settings.py中激活DOWNLOADER_MIDDLEWARES。同时想取消默认的一些中间件,也可以设置为None。注意的是激活DOWNLOADER_MIDDLEWARES并不会覆盖DOWNLOADER_MIDDLEWARES_BASE,而是继续串联起来
OWNLOADER_MIDDLEWARES = {
'myproject.middlewares.CustomDownloaderMiddleware': 543, 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
}
自定义下载中间件
在创建项目后,再项目文件夹中有一middlewares.py文件,里面自动生成了两个中间件示例或者说模板。我们如果要自定义中间件的话,可以在给的示例上修改,或者新建类实现方法,或者继承已有的中间件重写方法
以下是下载中间件可以实现的方法,在自定义中间件时,可以根据需求实现
1.process_request(self, request, spider)
当每个request通过下载中间件时,该方法被调用。process_request() 必须返回其中之一: 返回 None 、返回一个 Response 对象、返回一个 Request 对象或raise IgnoreRequest 。最常使用的是返回None
- 如果其返回 None ,会将处理过后的request丢给中间件链中的下一个中间件的process_request()方法处理,直到丢到下载器,由下载器下载
- 如果其返回 Response 对象,Scrapy将不会调用任何其他的 process_request() 或 process_exception() 方法,也不会丢到下载器下载;直接将其返回的response丢到中间件链的process_response()处理。可以通过scrapy.http.Response构建Response
- 如果其返回 Request 对象,Scrapy则停止调用process_request方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。
- 如果其raise一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。
参数:
request(Request 对象)–处理的request
spider(Spider 对象)–该request对应的spider
2.process_response(self, request, response, spider)
当下载的response返回时,process_response()被调用,且 必须返回以下之一: 返回一个 Response 对象、 返回一个 Request 对象或raise一个 IgnoreRequest 异常。
- 如果其返回一个 Response (可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。
- 如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。
- 如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。
参数:
request (Request 对象) – response所对应的request
response (Response 对象) – 被处理的response
spider (Spider 对象) – response所对应的spider
3.process_exception(self, request, exception, spider)
当下载处理器(download handler)或 process_request() (下载中间件)抛出异常(包括IgnoreRequest异常)时,Scrapy调用 process_exception() 。process_exception() 应该返回以下之一: 返回 None 、 一个 Response 对象、或者一个 Request 对象。
- 如果其返回 None ,Scrapy将会继续处理该异常,接着调用已安装的其他中间件的 process_exception() 方法,直到所有中间件都被调用完毕,则调用默认的异常处理。
- 如果其返回一个 Response 对象,则已安装的中间件链的 process_response() 方法被调用。Scrapy将不会调用任何其他中间件的 process_exception() 方法。
- 如果其返回一个 Request 对象, 则返回的request将会被重新调用下载。这将停止中间件的 process_exception() 方法执行,就如返回一个response的那样。
参数:
request (是 Request 对象) – 产生异常的request
exception (Exception 对象) – 抛出的异常
spider (Spider 对象) – request对应的spider
4.from_crawler(cls, crawler)
如果存在,则调用此类方法创建中间件实例Crawler。它必须返回一个新的中间件实例。Crawler对象提供对所有Scrapy核心组件的访问,如设置和信号; 它是中间件访问它们并将其功能挂钩到Scrapy的一种方式。
参数 :crawler(Crawlerobject)- 使用此中间件的爬网程序
开启自定义下载中间件
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.CustomDownloaderMiddleware': 543,
}
****不是每个中间件都需要开启
class MiddleproDownloaderMiddleware(object):
#UA池 list
# 设置ua池 user_agent_list = [‘。。。。’,‘....’]
#设置http和https的代理 list
#HTTP_Llist = [....]
#HTTPS_Llist = [....]
#拦截请求(process:过程)
def process_request(self, request, spider):
#UA伪装:封装一个ua池从中随机选取一个
request.headers["User-Agent"] = random.choice(self.user_agent_list)
return None
#拦截所有响应
def process_response(self,request,response,spider):
return response
#拦截所有异常的请求
def process_exception(self,request,exception,spider):
#代理ip(请求被拒后更换ip)
if request.url.split(":")[0] == 'http':
request.meta['proxy'] ='http://'+ random.choice(self.http的代理池)
else:
request.meta['proxy'] ='https://'+ random.choice(self.https的代理池)
return request
-拦截响应:篡改响应数据,响应对象,遇到post请求的ajax数据时
当全站爬取数据后,遇到ajax请求时,不能直接获取,需要通过拦截相应,
用selenium来获取数据并返回再进行处理
'''在spider中定义selenium,实例化一个浏览器对象'''
class WangyiSpider(scrapy.Spider):
models_url=[] # 存储响应数据为ajax动态加载等的网址
#实例化一个浏览器对象
def __init__(self):
self.bros = webdriver.Chrome(executable_path='/User/驱动文件的地址')
''''''''中间的获取到的响应在响应的方法中进行处理并返回获取到的值''''''''
#爬虫完成关闭时执行
def closed(self,spider):
self.bros.quit()
''' import scrapy.spiders.init 一定要导入 不然spider中不会有该属性
1.通过selenium的page_source来获取完整网页html
2.再通过HttpResponse 要实例化一个响应对象,(from scrapy.http import HtmlResponse)
把完整的html响应给spider
参数: HtmlResponse(url,body,encoding,request)
'''
def process_response(self, request, response, spider):
'''spider 爬虫类,在爬虫文件设置一个属性存储url,在这里可以直接获取
spider.models_url :models_url是spider爬虫类中定义保存ajax请求的网址
通过比较请求的url和存储的是否一样,是就进行seleium来获取,
不是则直接返回response
'''
#获取爬虫类中定义的浏览器对象
bro = spider.bros
if request.url in spider.models_url:
bro.get(request.url)
page_text = bro.page_source #通过page_source属性来获取
new_response = HtmlResponse(url=request.url,body=page_text,
encoding=utf-8,request=request)
#将获取到的新的响应返回给spider
return new_response
else:
return response #如果不是里边的则默认返回
=========================================================================
settings
- ROBOTSTXT_OBEY = False #robot协议,true为遵守,false不遵守
- LOG_LEVEL="ERROR" #执行爬虫时不打印日志文件
- ITEM_PIPELINES = { 'shici_new.pipelines.ShiciNewPipeline': 300,} # 开启管道进行数据存储
- DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.CustomDownloaderMiddleware': 543,} #开启下载中间件
- COOKIES_ENABLED=false #禁止使用cookies 规避被禁用
- DOWNLOAD_DELAY = 4 # 有些网站设置了高频分析,访问频率过快会判定为自动爬取值为4 代表 4秒
- 其余
=========================================================================
scrapy命令:
- scrapy startproject <project_name> [project_dir]:创建一个名为
project_name
下project_dir
目录。如果project_dir
没有指定,project_dir
将与project_name
.- 用法:
$ scrapy startproject myproject
- 用法:
- scrapy genspider [-t template] <name> <domain>:在当前文件夹或当前项目的
spiders
文件夹(如果从项目内部调用)。这个<name>
参数设置为spider的name
,同时<domain>
用于生成allowed_domains
和start_urls
蜘蛛的属性。- 用法
-
$ scrapy genspider -l Available templates: basic crawl csvfeed xmlfeed $ scrapy genspider example example.com Created spider 'example' using template 'basic' $ scrapy genspider -t crawl scrapyorg scrapy.org Created spider 'scrapyorg' using template 'crawl'
-
- 用法
- scrapy view <url> :在浏览器中打开给定的URL,因为您的废蜘蛛会“看到”它。有时候蜘蛛看到的页面与普通用户不同,所以这可以用来检查蜘蛛“看到”什么,并确认它是你所期望的。
-
--spider=SPIDER
:绕过Spider自动检测并强制使用特定Spider -
--no-redirect
:不遵循HTTP 3xx重定向(默认为遵循它们) -
用法:
$ scrapy view http://www.example.com/some/page.html [ ... browser starts ... ]
-
- scrapy runspider <spider_file.py> :行一个包含在python文件中的spider,而不必创建一个项目。
-
用法:
$ scrapy runspider myspider.py [ ... spider starts crawling ... ]
-
- scrapy settings [options] :获取 Scrapy 设置的值。
- 用法:
$ scrapy settings --get BOT_NAME
- 用法: