一切为了数据挖掘的准备
在中国大学MOOC网站上学习的北京理工大学嵩天老师的免费爬虫课程课件,简单易懂,感兴趣的戳 嵩天老师爬虫课程。侵删
八 scrapy框架
- 是一个框架不是一个函数库
- 是实现爬虫功能的软件结构和功能组件集合
- pip安装后测试 scrapy -h
8.1模块介绍
8.1.1 五个模块两个中间件
- spiders
- engine
- scheduler
- downloader
- item pipelines
- spiders和engine间的中间件模块
- downloader和engine间的中间件模块
8.1.2三个主要的数据流路径
- engine从spider获取爬取请求(requests),engine将请求转发给scheduler,对请求进行调度
- scheduler将下一个用户真实的爬取数据的请求发送给engine,engine通过中间件将请求发给downloader模块,downloader通过互联网爬取网页,将爬取结果生成response对象,传给engine,再通过中间件返给spider
- spiders处理从downloader获取的数据获得爬取项items,或新的爬取需求requests;由engine将items传给item pipelines,将新的requests发送给scheduler
8.1.3 五个模块详情
- spiders(框架的入口),需要用户配置
- 产生额外的爬取请求(request)
- 解析downloader返回的响应(response)
- 产生爬取项(scraped item)
- engine,不需要修改
- 控制所有模块间的数据流
- 根据条件触发事件
- scheduler,不需要修改
- 对请求调度管理
- downloader,不需要修改
- 根据请求下载网页
- item pipelines(框架出口),需要用户编写配置代码
- 以流水线处理spider产生的爬取项
- 由一组操作顺序组成,类似流水线,每个操作是一个item pipline类型
- 可能的操作包括:清理、检验和查重爬取项中的html数据、将数据存储到数据库中
- spiders和engine间的中间件模块,Spider Middleware
- 对请求和爬取项的再处理,修改、丢弃、新增请求或爬取项
- downloader和engine间的中间件,downloader Middleware(用户可编写)
- 实施engine\scheduler\downloader之间进行用户可配置的控制,修改、丢弃、新增请求或响应
8.2 scrapy和requests对比
8.2.1 共同点
- 都对页面请求和爬取
- 都没有处理js、提交表单、应对验证码等功能
8.2.3 不同点
requests | scrapy |
---|---|
页面级爬虫 | 网站级爬虫 |
功能库(函数库) | 一个框架 |
并发性考虑不足,性能较差 | 并发行好,性能较高 |
重点在于页面下载 | 重点在于爬虫结构 |
定制灵活 | 一般定制灵活,深度定制困难 |
注意:
- 爬取速度快不一定好,有可能被反爬虫发现
- 当定制需求很高时,可以考虑自搭框架
8.3 scrapy的常用命令
命令格式
scrapy <command> [options] [args]
8.3.1 常用命令
- startproject,创建新工程
scrapy startproject <name> [dir]
- genspider,创建一个爬虫
scrapy genspider [options] <name> <domain>
- crawl,运行一个爬虫
scrapy crawl <spider>
- settings,获取爬虫配置信息
scrapy settings [options]
- list,列出工程中所有爬虫
scrapy list
- shell,启动URL调试命令行
scrapy shell [url]
九、scrapy爬虫基本使用
9.1 scrapy爬虫的数据类型
9.1.1 scrapy.http.Request()类
表示一个http请求,由spider生成,downloader执行
属性或方法:
- .url,Request对应的请求url地址
- .encoding
- .body,请求内容主题,字符串类型
- .meta,用户添加的扩展信息,在scrapy内部模块间传递信息使用
- .copy(),复制该请求
- .replace(),创建新的request
9.1.2 scrapy.http.Response()类
表示一个http响应,由downloader生成,由spider处理
属性或方法:
- .url,Response对应的请求url地址
- .body,Response对应的内容信息,字符串类型
- .copy(),复制改响应
- .text
- .meta
9.1.3 scrapy.Item()类
表示从一个html页面提取的信息内容,由spider生成,由item pipeline处理。Item类似字典类型,可以按照字典类型操作
9.2 scrapy爬虫提取信息的方法
- BeautifulSoup
- lxml
- re
- XPath Selector
- CSS Selector
<html>.css('标签名称::attr(标签属性)').extract()
9.3 scrapy爬虫的使用步骤
应用scrapy爬虫框架主要是编写配置型代码
- 创建一个工程和spider模板
- 编写spider
- 编写Item Pipeline
- 优化配置策略
9.3.1 创建一个工程
scrapy startproject python123demo
会生成目录
- scrapy.cfg,部署scrapy爬虫的配置文件(配置了settings,工程名称和默认端口)
- python123 文件夹
- _init_.py, 初始化脚本
- items.py,scrapy.Item的继承类
- middlewares.py, Middlewares的代码模板(继承类)
- pipelines.py,pipelines的代码模板(继承类)
- settings.py, scrapy爬虫的配置文件()
- spiders 文件夹,spiders代码模块目录
- _init_.py,初始文件,无需修改
- _pycache_,缓存目录
9.3.2 在工程中产生一个scrapy爬虫
cd python123demo
scrapy genspider demo python123.io
- 会在spiders目录下生成一个demo.py文件(此文件也可以手动创建)
- 生成的spider名称为demo
- 在名称为demo的spider中,默认访问域名为python123.io
- demo.py中代码
import scrapy
class DemoSpider(scrapy.Spider):
# 此爬虫的名字为demo
name = 'demo'
# 默认域名,此行代码可以注释掉
allowed_domains = ['python123.io']
# 爬取的初始页面,修改此url即可
start_urls = ['http://python123.io/']
# 用于处理响应,解析页面,处理来自downloader的Response
# 生成scrapy.Item(字典);或发现新的url爬取请求
def parse(self, response):
pass
9.3.3 配置产生的spider爬虫
配置初始的url,解析response的页面
import scrapy
class DemoSpider(scrapy.Spider):
name = 'demo' #爬虫的名字
start_urls = ['http://python123.io/ws/demo.html']
# 会默认使用parse方法解析页面
def parse(self, response):
fname = response.url.split('/')[-1]
with open(fname,'wb') as f:
f.write(response.body)
self.log('Saved file %s.' % fname)
此代码可以用yield生成器更高效,节省空间
import scrapy
class DemoSpider(scrapy.Spider):
name = 'demo' #爬虫的明细
# start_requests为scrapy.Spider类继承函数
def start_requests(self):
#一个list
urls = ['http://python123.io/ws/demo.html']
for url in urls:
# Request返回的Response结果回调给self.parse函数
yield scrapy.Request(url = url,callback = self.parse)
# 解析初始页面的访问response
def parse(self, response):
fname = response.url.split('/')[-1]
with open(fname,'wb') as f:
f.write(response.body)
self.log('Saved file %s.' % fname)
十、scrapy实例与requests实例对比
从http://quote.eastmoney.com/stock_list.html获取深交所和上交所所有股票的名称,从https://gupiao.baidu.com/stock获取股票的详细信息,保存到文件中
10.1 scrapy实例
编写spider处理链接爬取和页面解析,编写pipelines处理信息存储
10.1.1 创建工程和spider模板
cmd命令行
scrapy startproject BaiduStocks
cd BaiduStocks
scrapy genspider stocks baidu.com
10.1.2 编写spider
配置stocks.py文件
import scrapy
import re
class StockSpider(scrapy.Spider):
name = 'stock'
start_urls = ['http://quote.eastmoney.com/stock_list.html']
#初始的Request请求返回Response后默认调用parse函数,
# 此函数用于生成新的请求,解析response
def parse(self,response):
for href in response.css('a::attr(href)').extract():
try:
stock = re.findall(r'[s][hz]\d{6}',href)[0]
url = 'https://gupiao.baidu.com/stock/' + stock + '.html'
# 生成新的访问请求,返回response后回调parse_stock函数
# 并且设置user agent,避免robots协议的限制
yield scrapy.Request(url, callback=self.parse_stock,headers={'User-Agent':'Mozilla/5.0'})
except:
continue
#生成字典类型数据,最终会给到Item pipelines模块
def parse_stock(self,response):
infoDict = {}
stockInfo = response.css('.stock-bets')
#股票名称
name = stockInfo.css('.bets-name').extract[0]
#股票参数
keylist = stockInfo.css('dt').extract()
valuelist = stockInfo.css('dd').extract()
for i in range(len(keylist)):
key = re.findall(r'>.*</dt>',keylist[i])[0][1:-5]
try:
val = re.findall(r'\d+\.?.*</dd>',valuelist[i])[0][0:-5]
except:
val = '--'
infoDict[key] = val
infoDict.update({'股票名称':re.findall('\s.*(',name)[0].split()[0] + re.findall('\>.*\<',name)[0][1:-1]})
# infoDict会传递到pipelines文件中
yield infoDict
10.1.3 编写pipelines
配置pipelines.py文件。自己定义一个对爬取项(Item)的处理类,包括三个函数
#自定义的pipeline类,去处理每个spider的字典结果
class BaidustocksInfoPipeline(object):
#每个爬虫被调用时,每个pipeline对应的方法
def open_spider(self,spider):
self.f = open('BaiduStockInfo.txt','w')
#每个爬虫关闭时,pipeline对应的方法
def close_spider(self,spider):
self.f.close()
#对每个item处理的方法(把股票信息写入文件)
def process_item(self,item,spider):
try:
line = str(dict(item)) + '\n'
self.f.write(line)
except:
pass
return item
10.1.4 配置settings.py文件
ITEM_PIPELINES = {
'BaiduStocks.pipelines.BaidustocksInfoPipeline': 300,
}
10.1.5 程序执行
scrapy crawl stocks
10.1.6 被爬网站有反爬限制时
此时请求页面的返回status_code为403;可以在settings文件中做设置
- 方法一:设置user agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
- 方法二:设置IP代理
PROXIES = [
{'ip_port': '111.11.228.75:80', 'user_pass': ''},
{'ip_port': '120.198.243.22:80', 'user_pass': ''},
{'ip_port': '111.8.60.9:8123', 'user_pass': ''},
{'ip_port': '101.71.27.120:80', 'user_pass': ''},
{'ip_port': '122.96.59.104:80', 'user_pass': ''},
{'ip_port': '122.224.249.122:8088', 'user_pass': ''},
]
- 方法三:修改是否遵守robots协议和是否缓存
ROBOTSTXT_OBEY = False
COOKIES_ENABLED = False
- 方法四:设置延迟,让网页访问速度频率类人,比较有效,不过爬取网页量大时,真的贼慢
DOWNLOAD_DELAY = 3
10.1.7 配置并发连接选项
在settings.py文件中设置
- CONCURRENT_REQUESTS,代表downloader最大并发请求下载数量,默认32
- CONCURRENT_ITEMS, Item pipeline最大并发item处理数量,默认100
- CONCURRENT_REQUESTS_PER_DOMAIN,每个域名最大的并发请求数量,默认8
- CONCURRENT_REQUESTS_PER_IP,每个目标IP最大的并发请求数量,默认0,非0有效
10.2 requests实例
import re
from bs4 import BeautifulSoup as bs
import requests
import traceback
def getHTMLText(url,code = 'utf-8'):
try:
r = requests.get(url,timeout=30)
r.raise_for_status()
# 将编码识别的优化,r.encoding是页面header部分的编码,r.apparent_encoding是程序识别的编码,如果一直用程序识别会浪费识别。当已确定编码时,可以自己设定
# r.encoding = r.apparent_encoding
r.encoding = code
return r.text
except:
return ''
def getStockList(lst,stockURL):
html = getHTMLText(stockURL,'GB2312')
soup =bs(html,'html.parser')
a = soup.find_all('a')
# 深交所sz开头,上交所sh开头,股票代码中数字为6位
for i in a:
try:
href = i.attrs['href']
lst.append(re.findall(r'[s][hz]\d{6}',href)[0])
except:
continue
def getStockInfo(lst,stockURL, fpath):
"""
stockURL个股链接
"""
count = 0
for stock in lst:
url = stockURL + stock + '.html'
html = getHTMLText(url)
try:
if html == '':
continue
infoDict = {}
soup = bs(html,'html.parser')
stockInfo = soup.find('div',attrs = {'class':'stock-bets'})
# 获取股票名称,及当前价格
name = stockInfo.find_all('a',attrs = {'class':'bets-name'})[0]
infoDict.update({'股票名称':name.text.split()[0]})
keylist = stockInfo.find_all('dt')
valuelist = stockInfo.find_all('dd')
for i in range(len(keylist)):
key = keylist[i].text
val = valuelist[i].text
infoDict[key]=val
with open(fpath,'a',encoding='utf-8') as f:
f.write(str(infoDict) + '\n')
count = count + 1
#动态变换的进度,\r光标挪动到行的最前方
print('\r当前速度:{:.2f}%'.format(count*100/len(lst)),end='')
except:
traceback.print_exc()
count = count + 1
print('\r当前速度:{:.2f}%'.format(count*100/len(lst)),end='')
def main():
stock_list_url = "http://quote.eastmoney.com/stock_list.html"
stock_info_url = 'https://gupiao.baidu.com/stock/'
filename = "C:\\Users\\lenovo\\Desktop\\python\\baidustockinfo.txt"
#股票列表
slist=[]
getStockList(slist,stock_list_url)
#存储到相关文件
getStockInfo(slist,stock_info_url,filename)
main()
被爬网站有反扒限制部分转自https://blog.csdn.net/weixin_41931602/article/details/80200695