爬虫系列三


一切为了数据挖掘的准备
在中国大学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,对请求进行调度
requests
spider
engine
scheduler
  • scheduler将下一个用户真实的爬取数据的请求发送给engine,engine通过中间件将请求发给downloader模块,downloader通过互联网爬取网页,将爬取结果生成response对象,传给engine,再通过中间件返给spider
requests
requests
response
response
scheduler
engine
downloader
spider
  • spiders处理从downloader获取的数据获得爬取项items,或新的爬取需求requests;由engine将items传给item pipelines,将新的requests发送给scheduler
items,requests
items
requests
spiders
engine
itemPiplines
schedule
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的页面

扫描二维码关注公众号,回复: 6168333 查看本文章
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

猜你喜欢

转载自blog.csdn.net/liuerin/article/details/89706900