文章目录
- 单元一 Requests库
- Requests库get()方法
- Requests库的安装
- Requests主要方法
- 爬取网页的通用代码框架
- Robots协议
- robots协议遵守方式
- 网络爬虫引发的问题
- 京东商品页面的爬取
- 百度/360搜索关键词提交
- 网络图片的爬取和存储
- IP地址归属地的自动查询
- BeautifulSoup库安装
- Beautiful Soup库的基本元素
- 基于bs4库的HTML格式输出
- 基于bs4库的HTML内容遍历方法
- 基于bs4库的HTML内容查找方法
- 三种信息标记形式的比较
- 提取信息的一般方法
- 中国大学排名定向爬虫
- Re库的match对象
- Re库的基本使用
- Re库的贪婪匹配和最小匹配
- 正则表达式概念
- 正则表达式的语法
- 淘宝商品比价定向爬虫
- 股票数据定向爬虫
- requests库和Scrapy爬虫比较
- Scrapy爬虫的常用命令
- Scrapy爬虫框架介绍
- Scrapy爬虫框架解析
- Scrapy实例1
- Scrapy爬虫的基本使用
- yield关键字的使用
- 股票数据Scrapy爬虫实例
单元一 Requests库
HTTP协议及Requests库方法
HTTP协议:Hypertext Transfer Protocal,超文本传输协议,基于响应与请求、无状态的应用层协议
HTTP协议采用URL作为定位网络资源的标识
每个URL格式:http://host[:port][path]
host:一个合法的Internet主机域名或IP地址
port:端口号,缺省为80
path:请示资源的路径
URL通过HTTP协议存取资源的Internet路径,一个URL对应一个资源
HTTP协议方法:
PATCH和PUT的区别:
若URL有一组数据UserInfo,包含UserID、UserName等20个字段,要修改UserName,其他不变,PATCH仅向URL提交UserName的局部更新请示,PUT必须要提交所有字段,未提交的字段被删除。所以PATCH方法可以节省带宽
Requests库的post()方法
payload = {'key1':'value1','key2':'value2'}
r = requests.post('http://httpbin.org/post',data = payload)
print(r.text)
键值对默认存到表单下
r = requests.post('http://httpbin.org/post',data = 'ABC')
print(r.text)
字符串自动编码为data
put()方法会覆盖原有的数据
Requests库get()方法
url = 某个网址
r = requests.get(url)
request()构造一个向服务器请求资源的Requests对象
Response返回一个包含服务器资源的Requests对象
requests.get(url,params=None,**kwargs)
url:拟获取页面的url链接
params:url中的额外参数,字典或字节流格式,可选
**kwargs:12个控制访问的参数
Requests库共有7个常用方法,除了requests()方法为基础方法外,其他方法都是调用requests()方法
import requests
r = requests.get("http://www.baidu.com")
print(r.status_code)
type(r)
r.headers
状态码为200表示成功
Response对象的属性
import requests
r = requests.get("http://www.baidu.com")
r.status_code
r.encoding
r.apparent_encoding
r.encoding = 'utf-8'
r.text
如果header中不存在charset,encoding则认为编码为ISO-8859-1,它不能解析中文
Requests库的安装
cmd控制台:pip install requests
Requests主要方法
requests.request(method,url,**kwargs)
method:请求方法,如get/put/OPTIONS等7种
url:链接
**kwargs:控制访问参数,13个
r = requests.request('GET',url,**kwargs)
r = requests.request('HEAD',url,**kwargs)
r = requests.request('POST',url,**kwargs)
r = requests.request('PUT',url,**kwargs)
r = requests.request('PATCH',url,**kwargs)
r = requests.request('delete',url,**kwargs)
r = requests.request('OPTIONS',url,**kwargs)
**kwargs:
params:字典或字节序列,作为参数增加到url中
kv = {'key1':'value1','key2':'value2'}
r = requests.request('GET','http://python123.io/ws',params=kv)
print(r.url)
data:字典、字节序列或文件对象,作为Request的内容
kv = {'key1':'value1','key2':'value2'}
r = requests.request('GET','http://python123.io/ws',data=kv)
body ='主体内容'
r = requests.request('POST','http://python123.io/ws',data = body)
json:JSON格式的数据,作为Reuqest的内容
kv = {'key1':'value1'}
r = requests.request('POST','http://python123.io/ws',json= kv)
headers:字典,HTTP定制头
hd={'user-agent':'Chrome/10'}
r = requests.request('POST','http://python123.io/ws',headers= hd)
cookies:字典或CookieJar,Request中的cookie
auth:元组,支持HTTP认证
files:字典类型,传输文件
fs = {'file':open('data.xls', 'rb')}
r = requests.request('POST','http://python123.io/ws',files= fs)
timeout:设定超时时间,以秒为单位
r = requests.request('GET','http://python123.io/ws',timeout=10)
proxies:字典类型,设定访问代理服务器,可以增加登录认证
pxs = {'http':'http://user:pass@10.10.10.1:1234'
'https':'https://10.10.10.1:4321'}
r = requests.request('GET','http://www.baidu.com',proxies=pxs)
allow_redirects:True/False,默认为True,重定向开关
stream:True/False,默认为True,获取内容立即下载开关
verify:True/False,默认为True,认证SSL证书开关
cert:本地SSL证书路径
requests.get(url,params=None,**kwargs)
**kwargs:12个参数,即request中除了params外的其他参数
requests.head(url,**kwargs)
**kwargs:13个参数
requests.post(url,data=None,json=None,**kwargs)
data:字典、字节序列或文件,Request的内容
json:JSON格式的数据,Request的内容
**kwargs:11个参数
requests.put(url,data=None,**kwargs)
data:字典、字节序列或文件,Request的内容
**kwargs:12个参数
requests.patch(url,data=None,**kwargs)
data:字典、字节序列或文件,Request的内容
**kwargs:12个参数
requests.delete(url,**kwargs)
**kwargs:13个参数
爬取网页的通用代码框架
Requests库的异常
import requests
def geHTMLText(url):
try:
r = requests.get(url,timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return "产生异常"
if __name__ == "__main__":
url = "http://www.baidu.com"
print(getHTMLText(url))
Robots协议
Robots Exclusion Standard 网络爬虫排除标准
robots.txt在网站的根目录下
*表示所有
Disallow: /?*表示所有都不允许访问以?开头的路径
Disallow:/pop/*.html表示所有都不允许访问pop下的所有html
User-agent:EtaoSpider
Disallow: /
表示EtaoSpider不允许爬取任何内容
如果一个网址不提供robots协议,则默认允许所有爬虫爬取
robots协议遵守方式
robots协议是建议但非约束性,不遵守存在法律风险
类人行为可不参考Robots协议,访问量非常小,速度慢
网络爬虫引发的问题
小规模,可用requests库
中规模,数据规格大,速度敏感,可用Scrapy
爬取全网,如搜索引擎,定制开发
网络爬虫的骚扰,服务器崩溃
服务器上的数据有产权归属
爬虫泄露隐私
爬虫限制:
来源审查:判断User-agent
robots协议:告示牌
京东商品页面的爬取
import requests
url = "https://item.jd.com/2967929.html"
try:
r = requests.get(url)
r.raise_for_status()
r.encoding = r.apparent_encoding
print(r.text[:1000])
except:
print("爬取失败")
百度/360搜索关键词提交
import requests
keyword = "Python"
try:
kv = {'wd':keyword}
r = requests.get("http://www.baidu.com/s",params =kv)
print(r.requests.url)
r.raise_for_status()
print(len(r.text))
except:
print("爬取失败")
网络图片的爬取和存储
import requests
import os
url = "http://image.nationalgeographic.com.cn/2017/0211/20170211061910157.jpg"
root ="D://pics//"
path = root + url.split('/')[-1]
try:
if not os.path.exists(roo):
os.mkdir(root)
if not os.path.exists(path):
r = requests.get(url)
with open(path,'wb') as f:
f.write(r.content)
f.close()
print("文件保存成功")
else:
print("文件已存在")
except:
print("爬取失败")
IP地址归属地的自动查询
import requests
url = "http://m.ip138.com/ip.asp?ip="
try:
r = requests.get(url+'202.204.80.122')
r.raise_for_status()
r.encoding = r.apparent_encoding
print(r.text[-500:])
except:
print("爬取失败")
BeautifulSoup库安装
cmd下,输入pip install beautifulsoup4
html的格式
<html>
<head></head>
<body>
<\body>
</html>
import requests
from bs4 import BeautifulSoup
r = requests.get("http://python123.io/ws/demo.html")
r.text
demo = r.text
soup = BeautifulSoup(demo,"html.parser")
print(soup.prettify())
Beautiful Soup库的基本元素
此库是解析、遍历、维护标签树的功能库
<p class=“title”>…</p>
名称是p
属性是class=“title”
解析器:
基本元素
from bs4 import BeautifulSoup
soup = BeautifulSoup(demo,"html.parser")
soup.title
tag = soup.a
tag.attrs
tag.attrs['class']
tag.attrs['href']
type(tag.attrs)
type(tag)
soup.a.name
soup.a.parent.name
soup.a.parent.parent.name
soup.a.string
soup.p
soup.p.string
type(soup.p.string)
newsoup = BeautifulSoup("<b>--Tis is a comment</b><p>This is not a comment</p>","html.parser")
newsoup.b.string
type(newsoup.b.string)
newsoup.p.string
type(newsoup.p.string)
基于bs4库的HTML格式输出
from bs4 import BeautifulSoup
soup = BeautifulSoup(demo,"html.parser")
soup.prettify()
print(soup.prettify())
基于bs4库的HTML内容遍历方法
标签树的下行遍历
soup = BeautifulSoup(demo,"html.parser")
soup.head
soup.head.contents
soup.body.contents
len(soup.body.contents)
soup.body.contents[1]
上行遍历
soup = BeautifulSoup(demo,"html.parser")
soup.title.parent
soup.html.parent
soup.parent
上行遍历
soup = BeautifulSoup(demo,"html.parser")
for parent in soup.a.parents:
if parent is None:
print(parent)
else:
print(parent.name)
平行遍历
平行遍历发生在同一父节点下的各节点间
soup = BeautifulSoup(demo,"html.parser")
soup.a.next_sibling
soup.a.next_sibling.next.sibling
soup.a.previous_sibling
soup.a.previous_sibling.previous_sibling
soup.a.parent
基于bs4库的HTML内容查找方法
<>.find_all(name,attrs,recursive,string,**kwargs)
返回一个列表类型,存储结果
name:对标签名称的检索字符串
attrs:对标签属性值的检索字符串,可标注属性检索
recursive:是否对子孙全部检索,默认是True
string:<>…</>中字符串区域的检索字符串
soup.find_all('a')
soup.find_all(['a','b'])
若只给True,则返回所有标签
for tag in soup.find_all(True):
print(tag.name)
soup.find_all('p','course')
精准查找:
soup.find_all(id='link1')
模糊查找:正则表达式
soup.find_all(string = "Basic Python")
简写:
<tag>()等价于<tag>.find_all()
扩展方法
三种信息标记形式的比较
XML:eXtensible Markup Language
用<>标记
<name>…</name>
JSON:JavaScript Object Notation
用有类型键值对标记
“key”:“value”
“key”:[“value1”,“value2”]
“key”:{“value1”,“value2”}
YAML:YAML Ain’t Markup Language
无类型键值对标记
firstName:xxx
lastName:XXX
XML:有效信息占比不高,大部分信息被标签占用,可用于Internet
JSON:比XML简洁,用于移动端或程序,无法体现注释
YAML:有效信息利用率最高,用于系统配置文件,有注释
提取信息的一般方法
一:完整解析后再提取关键信息
二:无视标记形式,直接搜索关键信息
from bs4 import BeautifulSoup
soup = BeautifulSoup(demo,"html.parser")
for link in soup.find_all('a'):
print(link.get('href'))
中国大学排名定向爬虫
import requests
import bs4
from bs4 import BeautifulSoup
def getHTMLText(url):
try:
r = requests.get(url,timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
def fillUnivList(ulist, html):
soup = BeautifulSoup(html, "html.parser")
for tr in soup.find('tbody').children:
if isinstance(tr,bs4.element.Tag):
tds = tr('td')
ulist.append([tds[0].string,tds[1].string,tds[2].string])
def printUnivList(ulist, num):
print("{:^10}\t{:^6}\t{:^10}".format("排名","学校名称","总分"))
for i in range(num):
u=ulist[i]
print("{:^10}\t{:^6}\t{:^10}".format(u[0],u[1],u[2]))
def main():
uinfo = []
url = "http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html"
html = getHTMLText(url)
fillUnivList(uinfo, html)
printUnivList(uinfo, 20)
main()
Re库的match对象
Match对象是一次匹配的结果,有type(match)将返回<class ‘_sre.SRE_Mathc’>
Match对象的属性
Match对象的方法
import re
m = re.search(r'[1-9]\d{5}','BIT10081 TSU100084')
m.string
m.re
m.pos
m.endpos
m.group(0)
m.start()
m.end()
m.span()
Re库的基本使用
用于字符串匹配
raw string类型(原生字符串类型),表达为r’text’,它不包含转义符的字符串,比如\\仅用\表示
Re库主要功能函数
re.search(pattern,string,flags=0)
pattern:正则表达式字符串
string:待匹配的字符串
flags:正则表达式使用时的控制标记
import re
m = re.search(r'[1-9]\d{5}','BIT 10081')
if match:
print(match.group(0))
re.match(pattern,string,flags=0)
import re
match = re.match(r'[1-9]\d{5}','BIT 10081')
if match:
match.group(0)
match = re.match(r'[1-9]\d{5}','10081 BIT')
if match:
match.group(0)
match可以返回空,调用group会出错
re.findall(pattern,string,flags=0)
搜索字符串,以列表类型返回全部能匹配的子串
import re
ls = re.findall(r'[1-9]\d{5}','BIT10081 TSU100084')
ls
re.split(pattern,string,maxsplit=0,flags=0)
将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
import re
re.split(r'[1-9]\d{5}','BIT10081 TSU100084')
re.split(r'[1-9]\d{5}','BIT10081 TSU100084',maxsplit=1)
re.finditer(pattern,string,flags=0)
搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象
import re
for m in re.finditer(r'[1-9]\d{5}','BIT10081 TSU100084'):
if m:
print(m.group(0))
re.sub(pattern,repl,string,count=0,flags=0)
在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串
Re库的贪婪匹配和最小匹配
Re库默认采用贪婪匹配,即输出匹配最长的子串
match = re.search(r'PY'.*N','PYANBNCNDN')
match.group(0)
最小匹配
match = re.search(r'PY'.*?N','PYANBNCNDN')
match.group(0)
最小匹配操作符
正则表达式概念
regular expression
regex
re
正则表达式是用来简洁表达一组字符串的表达式
通用的字符串表达框架
简洁表达一组字符串的表达式
针对字符串表达“简洁”和“特征”思想的工具
判断某字符串的特征归属
正则表达式的语法
abc*:表示*前面的c可以出现0次或无限次,如ab,abc,abcc,…
abc+:表示+前面的c可以出现1次或无限次,如abc,abcc…
IP地址的正则表达式
淘宝商品比价定向爬虫
import requests
import re
def getHTMLText(url):
try:
r = requests.get(url, timeout = 30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
def parsePage(ilt,html):
try:
plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"',html)
tlt = re.findall(r'\"raw_title\"\:\".*?\"',html)
for i in range(plt):
price = eval(plt[i].split(':')[1])
title = eval(title[i].split(':')[1])
ilt.append([price,title])
except:
print("")
def printGoodsList(ilt):
tplt = "{:4}\t{:8}\t{:16}"
print(tplt.format("序号","价格","商品名称"))
count = 0
for g in ilt:
count = count + 1
print(tplt.format(count,g[0],g[1]))
def main():
goods = "书包"
depth = 2
start_url = 'https://s.taobao.com/search?q='+goods
infoList = []
for i in range(depth):
try:
url = start_ulr+'&s='+str(44*i)
html = getHTMLText(url)
parsePage(infoList, html)
except:
continue
printGoodsList(infoList)
main()
股票数据定向爬虫
import requests
from bs4 import BeautifulSoup
import traceback
import re
def getHTMLText(url):
try:
r = requests.get(url, timeout = 30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.txt
except:
return ""
def getStockList(lst, stockURL):
html = getHTMLText(stockURL)
soup = BeautifulSoup(html, 'html.parser')
a = find_all('a')
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):
for stock in lst:
url = stockURL + stock + ".html"
html = getHTMLText(url)
try:
if html == "":
continue
infoDict = {}
soup = BeautifulSoup(html,'html.parser')
stockInfo = soup.find('div',attrs={'class':'stock-bets'})
name = stockInfo.find_all(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')y
except:
traceback.print_exc()
continue
def main():
stock_list_url = 'http://quote.eastmoney.com/stocklist.html'
stock_Info_url = 'http://gupiao.baidu.com/stock/'
output_file = 'D://BaiduStockInfo.txt'
slist = []
getStockList(slist, stock_list_url)
getStockInfo(slist, stock_info_url, output_file)
main()
requests库和Scrapy爬虫比较
相同点:
都可以进行页面请求和爬取
可用性好
没有处理js,提交表单
不同:
requests:页面级爬虫,功能库,并发性不足,页面下载,定制灵活,简单
Scrqpy:网站级爬虫,框架,并发性好,爬虫结构,深度定制困难,稍难
Scrapy爬虫的常用命令
Scrapy命令行格式
>scrapy<command>[options][args]
常用命令
命令行更容易自动化,适合脚本控制
本质上,Scrapy是给程序员用的,功能更重要
Scrapy爬虫框架介绍
爬虫框架是实现爬虫功能的一个软件结构和功能组件集合
爬虫框架是一个半成品,能帮助用户实现专业网络爬虫
Scrapy爬虫框架解析
Engine:控制抽有模块之间的数据流;根据条件触发事件(不需要用户修改)
Downloader:根据请求下载网页(不需要用户修改)
Scheduler:对所有爬虫请求进行调度管理(不需要用户修改)
Downloader Middleware:修改、丢弃、新增请求或响应
Spider:解析Downloader返回的响应;产生爬取项;产生额外的爬取请求
Item Pipelines:以流水线方式处理Spider产生的爬取项;由一组操作顺序组成,类似流水线,每个操作是一个Item Pipeline类型;清理、检验和查重爬取项中的HTML数据,将数据存储到数据库(需要用户编写)
Spider Middleware:修改、丢弃、新增请求或爬取项
Scrapy实例1
安装好Scrapy库后
打开命令提示符
输入d:
输入cd pycodes
输入scrapy startproject python123demo
scrapy.cfg:部署Scrapy爬虫的配置文件
python123demo:Scrapy框架的用户自定义Python代码
_init_.py:初始化脚本
items.py:Items代码模板(继承类)
middlewares.py:Middlewares代码模板(继承类)
pipelines.py:Pipelines代码模块(继承类)
settings.py:Scrapy爬虫的配置文件
输入scrapy genspider demo python123.io
增加了一个新的文件demo.py,内容如下
# -*- coding: utf-8 -*-
import scrapy
class DemoSpider(scrapy.Spider):
name = "demo"
allowed_domains = ["python123.io"]
start_urls = ['http://python123.io/']
def parse(self, response):
pass
parse()用于处理响应,解析内容形成字典,发现新的URL爬取请求
修改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)
输入scrapy crawl demo
Scrapy爬虫的基本使用
创建一个工程和Spider模板
编写Spider
编写Item Pipeline
优化配置策略
Requests类
Response类
Item类
Scrapy爬虫提取信息的方法
BeautifulSoup
lxml
re
XPath Selector
CSS Selector
yield关键字的使用
生成器是一个不断产生值的函数
包含yield语句的函数是一个生成器
生成器每次产生一个值,函数被冻结,被唤醒后再产生一个值
def gen(n):
for i in range(n):
yield i**2
股票数据Scrapy爬虫实例
建立工程和Spider模板
编写Spider
编写ITEM Pipelines
输入:
scrapy startproject BaiduStocks
cd BaiduStocks
scrapy genspider stocks baidu.com
修改spiders/stocks.py
# -*- coding: utf-8 -*-
import scrapy
import re
class StockSpider(scrapy.Spider):
name = "stocks"
start_urls = ['http://quote.eastmoney.com/stocklist.html']
def parse(self, response):
for href in response.css('a::attrs(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(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
修改pipelines.py文件
class BaidustockPipeline(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
修改settings.py,找到一个参数ITEM_PIPELINES
ITEM_PIPELINES = {
'BaiduStocks.pipelines.BaidustocksInfoPipeline':300,
}
执行:scrapy crawl stocks