目录
1 Requests库入门
1.1 Requests库安装
pip install requests
Requests库的安装测试:
>>> import requests
>>> r = requests.get("http://www.baidu.com")
>>> r.status_code
200
>>> r.encoding = 'utf-8' #修改默认编码
>>> r.text #打印网页内容
1.2 HTTP协议
HTTP,Hypertext Transfer Protocol,超文本传输协议
HTTP是一个基于“请求与响应”模式的、无状态的应用层协议。
HTTP协议采用URL作为定位网络资源的标识。
URL格式:http://host[:port][path]
-
host:合法的Internet主机域名或IP地址
-
port:端口号,缺省端口为80
-
path:请求资源的路径
HTTP URL的理解:
URL是通过HTTP协议存取资源的Internet路径,一个URL对应一个数据资源。
HTTP协议对资源的操作:
方法 | 说明 |
---|---|
GET | 请求获取URL位置的资源 |
HEAD | 请求获取URl位置资源的响应消息报告,即获得该资源的头部信息 |
POST | 请求向URL位置的资源后附加新的数据 |
PUT | 请求向URL位置存储一个资源,覆盖原URL位置的资源 |
PATCH | 请求局部更新URL位置的资源,即改变该处资源的部分内容 |
DELETE | 请求删除URL位置存储的资源 |
1.3 Requests库方法
方法 | 说明 |
---|---|
requests.request() | 构造一个请求,支撑以下各方法的基础方法 |
requests.get() | 获取HTML网页的主要方法,对应于HTTP的GET |
requests.head() | 获取HTML网页头信息的方法,对应于HTTP的HEAD |
requests.post() | 向HTML网页提交POST请求的方法,对应于HTTP的POST |
requests.put() | 向HTML网页提交PUT请求的方法,对应于HTTP的PUT |
requests.patch() | 向HTML网页提交局部修改请求,对应于HTTP的PATCH |
requests.delete() | 向HTML网页提交删除请求,对应于HTTP的DELETE |
主要方法为request方法,其余6个都是由request方法封装。
request()方法:
requests.request(method,url,**kwargs)
- method:请求方式,对应get/put/post等7种
- url:拟获取页面的url链接
- **kwargs:控制访问的参数,共13个,均为可选项
13个控制访问参数分别为:
- params:字典或字节序列,作为参数增加到url中
- data:字典、字节序列或文件对象,作为Request的内容
- json:JSON格式的数据,作为Request的内容
- headers:字典,HTTP定制头
- cookies:字典或CookieJar,Request中的cookie
- auth:元祖,支持HTTP认证功能
- files:字典类型,传输文件
- timeout:设定超时时间,秒为单位
- proxies:字典类型,设定访问代理服务器,可以增加登录认证
- allow_redirects:True/False,默认为True,重定向开关
- stream:True/False,默认为True,获取内容立即下载开关
- verify:True/False,默认为True,认证SSL证书开关
- cert:本地SSL证书路径
get()方法:
requests.get(url,params=None,**kwargs)
- url:拟获取页面的url链接
- params:url中的额外参数,字典或字节流格式,可选
- **kwargs:12个控制访问的参数
该方法构造了一个向服务器请求资源的Request对象,返回了一个包含服务器资源的Response对象,Response对象包含爬虫返回的内容
Response对象的属性:
属性 | 说明 |
---|---|
r.status_code | HTTP请求的返回状态,200表示连接成 |
r.text | HTTP响应内容的字符串形式,即:url对应的页面内容 |
r.encoding | 从HTTP header中猜测的响应内容编码方式 |
r.apparent_encoding | 从内容中分析出的响应内容编码方式(备选编码方式) |
r.content | HTTP响应内容的二进制形式 |
head()方法:
requests.head(url,**kwargs)
- url:拟获取页面的url链接
- **kwargs:13个控制访问的参数
post()方法:
requests.post(url,data=None,json=None,**kwargs)
- url:拟更新页面的url链接
- data:字典、字节序列或文件,Request的内容
- json:JSON格式的数据,Request的内容
- **kwargs:11个控制访问的参数
put()方法:
requests.put(url,data=None,json=None,**kwargs)
- url:拟更新页面的url链接
- data:字典、字节序列或文件,Request的内容
- **kwargs:12个控制访问的参数
patch()方法:
同put()方法
delet()方法:
requests.delete(url,**kwargs)
- url:拟删除页面的url链接
- **kwargs:13个控制访问的参数
1.4 爬取网页的通用代码框架
Requests库的异常:
异常 | 说明 |
---|---|
requests.ConnectionError | 网络连接错误异常,如DNS查询失败、拒绝连接等 |
requests.HTTPError | HTTP错误异常 |
requests.URLRequired | URL缺失异常 |
requests.TooManyRedirects | 超过最大 重定向次数,产生重定向异常 |
requests.ConnectTimeout | 连接远程服务器超时异常 |
requests.Timeout | 请求URL超时,产生超时异常 |
r.raise_for_status() | 如果不是200产生异常requests.HTTPError |
import requests
def getHTMLText(url):
try:
r = requests.get(url,timeout=30)
r.raise_for_status() #如果不是200,引发HTTPError异常
r.encoding = r.apparent_encoding
return r.text
except:
return "产生异常"
if __name__ == "__main__":
url = "http://www.baidu.com"
print(getHTMLText(url))
1.5 Requests库实战
向360搜索提交关键词:
import requests
# 向搜索引擎进行关键词提交
url = "http://www.so.com/s"
try:
kv = {'q':'python'}
r = requests.get(url,params =kv)
print(r.request.url)
r.raise_for_status()
print(len(r.text))
except:
print("产生异常")
获取网络图片及存储:
import requests
import os
url = "http://image.ngchina.com.cn/2015/0323/20150323111422966.jpg"
root = "D:/pics/"
path = root + url.split('/')[-1]
try:
if not os.path.exists(root):
os.mkdir(root)
if not os.path.exists(path):
r = requests.get(url)
with open(path,'wb') as f:
f.write(r.content) # r.content返回二进制内容
f.close()
print("文件保存成功")
else:
print("文件已存在")
except:
print("爬取失败")
2 Scrapy库入门
2.1 Scrapy库安装
可以用anaconda安装scrapy,打开anaconda prompt,输入以下代码
conda install scrapy
报错:
CondaError: Downloaded bytes did not match Content-Length
url: https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/win-64/pywin32-227-py37he774522_1.conda
target_path: E:\Anaconda3\pkgs\pywin32-227-py37he774522_1.conda
Content-Length: 5808297
downloaded bytes: 2653872
问题是用conda安装包的时候下载包的长度不够导致安装包不成功,原因一般是在下载的时候速度较慢,通过更新配置的清华镜像源成功解决,具体配置方法请参考:Anaconda 镜像使用帮助
2.2 Scrapy爬虫框架
Scrapy不是一个函数功能库,而是一个爬虫框架。
Scrapy爬虫框架介绍:
- 爬虫框架是实现爬虫功能的一个软件结构和功能组件集合。
- 爬虫框架是一个半成品,能够帮助用户实现专业网络爬虫。
Scrapy爬虫框架解析:
Engine:不需要用户修改
- 控制所有模块之间的数据流
- 根据条件触发事件
Downloader:不需要用户修改
- 根据请求下载网页
Scheduler:不需要用户修改
- 对所有爬取请求进行调度管理
Downloader Middleware:用户可编写配置代码
- 目的:实施Engine、Scheduler和Downloader之间进行用户可配置的控制
- 功能:修改、丢弃、新增请求或响应
Spider:需要用户编写配置代码
- 解析Downloader返回的响应(Response)
- 产生爬取项(scraped item)
- 产生额外的爬取请求(Request)
Item Pipelines:需要用户编写配置代码
- 以流水线方式处理Spider产生的爬取项
- 由一组操作顺序组成,类似流水线,每个操作是一个Item Pipeline类型
- 可能操作包括:清理、检验、和查重爬取项中的HTML数据、将数据存储到数据库
Spider Middleware:用户可以编写配置代码
- 目的:对请求和爬取项的再处理
- 功能:修改、丢弃、新增请求或爬取项
2.3 Request库和Scrapy爬虫的比较
requests vs. Scrapy:
相同点:
- 两者都可以进行页面请求和爬取,Python爬虫的两个重要技术路线
- 两者可用性都好,文档丰富,入门简单
- 两者都没有处理js、提交表单、应对验证码等功能(可扩展)
不同点:
requests | Scrapy |
---|---|
页面级爬虫 | 网站级爬虫 |
功能库 | 框架 |
并发性考虑不足,性能较差 | 并发性好,性能较高 |
重点在于页面下载 | 重点在于爬虫结构 |
定制灵活 | 一般定制灵活,深度定制困难 |
上手十分简单 | 入门稍难 |
2.4 Scrapy爬虫的常用命令
Scrapy命令行:
Scrapy是为持续运行设计的专业爬虫框架,提供操作的Scrapy命令行
Scrapy常用命令:
命令 | 说明 | 格式 |
---|---|---|
startproject | 创建一个新工程 | scrapy startproject <name> [dir] |
genspider | 创建一个爬虫 | scrapy genspider [options] <name> <domain> |
settings | 获得爬虫配置信息 | scrapy setting [options] |
crawl | 运行一个爬虫 | scrapy crawl <spider> |
list | 列出工程中所有爬虫 | scrapy list |
shell | 启动URL调试命令行 | scrapy shell [url] |
2.5 Scrapy爬虫的第一个实例
步骤1:建立一个Scrapy爬虫工程
打开cmd,输入以下命令在工程目录创建一个新的Scrapy工程,工程名为python123demo
生成的工程目录:
python123demo/ ----------------> 外层目录
- scrapy.cfg ---------> 部署Scrapy爬虫的配置文件
- python123demo/ ---------> Scrapy框架的用户自定义Python代码
__init__.py ----> 初始化脚本
items.py ----> Items代码模板(继承类)
middlewares.py ----> Middlewares代码模板(继承类)
pipelines.py ----> Pipelines代码模板(继承类)
settings.py ----> Scrapy爬虫的配置文件
spiders/ ----> Spiders代码模板目录(继承类)
- __init__.py --------> 初始文件,无需修改
- __pycache__/ --------> 缓存目录,无需修改
步骤2:在工程中产生一个Scrapy爬虫
打开cmd,输入以下命令生成一个名为demo的爬虫
步骤3:配置产生的spider爬虫
修改D:\pycodes\python123demo\python123demo\spiders\demo.py文件
# -*- coding: utf-8 -*-
import scrapy
class DemoSpider(scrapy.Spider):
name = 'demo'
# allowed_domains = ['python123.io']
start_urls = ['http://python123.io/ws/demo.html']
def parse(self, response):
fname = response.url.split('/')[-1]
with open(fname, 'wb') as f:
f.write(response.body)
self.log('Save file %s.' % name)
demo.py代码的完整版本
import scrapy
class DemoSpider(scrapy.Spider):
name = "demo"
def start_requests(self):
urls = [
'http://python123.io/ws/demo.html'
]
for url in urls:
yield scrapy.Requests(url=url, callback=self.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)
步骤4:运行爬虫,获取网页
打开cmd,输入以下命令运行爬虫
2.6 Scrapy爬虫的基本使用
Scrapy爬虫的使用步骤:
- 步骤1:创建一个工程和Spider模板;
- 步骤2:编写Spider;
- 步骤3:编写Item Pipeline
- 步骤4:优化配置策略
2.7 Scrapy爬虫的数据类型
Request类:
class scrapy.http.Request()
- Request对象表示一个HTTP请求
- 由Spider生成,由Downloader执行
属性或方法 | 说明 |
---|---|
.url | Request对应的请求URL地址 |
.method | 对应的请求方法,’GET‘ ’POST‘等 |
.headers | 字典类型风格的请求头 |
.body | 请求内容主体,字符串类型 |
.meta | 用户添加的扩展信息,在Scrapy内部模块间传递信息使用 |
.copy() | 复制该请求 |
Response类:
class scrapy.http.Response()
- Response对象表示一个HTTP响应
- 由Downloader生成,由Spider处理
属性或方法 | 说明 |
---|---|
.url | Response对应的URL地址 |
.status | HTTP状态码,默认是200 |
.headers | Response对应的头部信息 |
.body | Response对应的内容信息,字符串类型 |
.flags | 一组标记 |
.request | 产生Response类型对应的Request对象 |
.copy() | 复制该响应 |
Item类:
class scrapy.item.Item()
- Item对象表示一个从HTML页面中提取的信息内容
- 由Spider生成,由Item Pipeline处理
- Item类似字典类型,可以按照字典类型操作
2.8 Scrapy爬虫提取信息的方法
Scrapy爬虫支持多种HTML信息提取方法:
- Beautiful Soup
- lxml
- re
- XPath Selector
- CSS Selector
CSS Selector的基本使用:
CSS Selector由W3C组织维护并规范
2.9 股票数据Scrapy爬虫实例
功能描述:
- 技术路线:scrapy
- 目标:获取上交所和深交所所有股票的名称和交易信息
- 输出:保存到文件中
步骤1:建立工程和Spider模板
- \>scrapy startproject BaiduStocks
- \>cd BaiduStocks
- \>scrapy genspider stocks baidu.com
- 进一步修改spiders/stocks.py文件
步骤2:编写Spider
- 配置stock.py文件
- 修改对返回页面的处理
- 修改对新增URL爬取请求的处理
修改spider.stocks.py文件使其能够解析返回的信息
# -*- coding: utf-8 -*-
import scrapy
import re
class StocksSpider(scrapy.Spider):
name = "stocks"
start_urls = ['https://quote.eastmoney.com/stocklist.html']
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'
yield scrapy.Request(url, callback=self.parse_stock)
except:
continue
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]})
yield infoDict
步骤3:编写Pipelines
- 配置pipelines.py文件
- 定义对爬取项(Scrapy Item)的处理类
- 配置ITEM_PIPELINES选项
修改pipelines.py
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
class BaidustocksPipeline(object):
def process_item(self, item, spider):
return item
class BaidustocksInfoPipeline(object):
def open_spider(self, spider):
self.f = open('BaiduStockInfo.txt', 'w')
def close_spider(self, spider):
self.f.close()
def process_item(self, item, spider):
try:
line = str(dict(item)) + '\n'
self.f.write(line)
except:
pass
return item
修改setting.py中的ITEM_PIPELINES使框架能找到我们新定义的类
# Configure item pipelines
# See https://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'BaiduStocks.pipelines.BaidustocksInfoPipeline': 300,
}
配置并发连接选项:
settings.py文件
选项 | 说明 |
---|---|
CONCURRENT_REQUESTS | Downloader最大并发请求下载数量,默认为32 |
CONCURRENT_ITEMS | Item Pipeline最大并发ITEM处理数量,默认为100 |
CONCURRENT_REQUESTS_PRE_DOMAIN | 每个目标域名最大的并发请求数量,默认为8 |
CONCURRENT_REQUESTS_PRE_IP | 每个目标IP最大的并发请求数量,默认为0,非0有效 |