urllib库
- python2中,由urllib和urllib2两个库来发送请求,python3中没有urllib2,统一为urllib
- urllib的几个模块
- request:最基本的http请求模块,
- error: 异常处理的模块
- parse:工具模块,提供了许多url处理方法,比如拆分、解析、合并等
- robotparser:识别网络的robot.txt文件
- request模块下的几个方法类
- urlopen(url,data,timeoit)
- url:必传,请求资源的地址
- data:可选,必须是字节流格式,使用此方法后不再是get请求,而是post请求
- timeout:设定超时时间
from urllib import request, parse, error # urlopen(url, data) resp = request.urlopen("http://shuyantech.com/api/cndbpedia/avpair?q=%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6") # 一个http响应对象 res = resp.read() # 返回值为bytes编码对象 res = res.decode("utf-8") # 对其解码 print(res) # data: 像url中动态传参数,使用此方法后不再是get请求,而是post请求 params = {"q":"清华大学"} # 定义参数 # urlencode(dict)将参数字典转化为字符串 data = parse.urlencode(params) # q=%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6 # bytes(str,encode)将字符串转化为字节流类型,并制定编码格式 data = bytes(data, encoding="utf8") # b'q=%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6' resp = request.urlopen("http://shuyantech.com/api/cndbpedia/avpair?", data) res = resp.read() # b'{"status": "ok", "ret": [["\xe4\xb8\xad\xe6\x96\x87\xe5\x90\x8d", "\xe6\xb8\x85\xe5\x8d\x8e\xe5\xa4\xa7\xe5\xad\xa6"], res = res.decode("utf-8") # {"status": "ok", "ret": [["中文名", "清华大学"], print(res) # timeout,设置超时时间,单位为秒 try: resp = request.urlopen("http://shuyantech.com/api/cndbpedia/avpair?q=%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6", timeout=0.01) print(resp) # urllib.error.URLError:
except error.URLError as e: print(e.reason) # timed out # isinstance,判断对象是否为已知类型,与type的区别,isinstance() 会认为子类是一种父类类型,考虑继承关系,type则不考虑继承关系 import socket if isinstance(e.reason, socket.timeout): print("超时了") - Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False,method=None)
- url:必传,请求的资源地址
- data:必须是bytes(字节流)类型的
- headers:字典形式,构造请求头信息,例如User-Agent
- method:字符串,指定请求方式
- origin_req_host:请求方的host和ip
- unverifiable:验证用户有没有权限接受请求结果
from urllib import request, parse url = "http://httpbin.org/post" data = { "name":"Request" } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36", "Host": "httpbin.org" } method = "POST" # 构造请求信息 req = request.Request(method=method, url=url, headers=headers, data=bytes(parse.urlencode(data), encoding="utf8") ) resp = request.urlopen(req) res = resp.read().decode("utf-8") print(res)
- Handler各种处理器和opener
- HTTPBasicAuthHandler:用于管理认证,当一个连接打开时,需要登录,可以用它来解决认证问题
- HTTPCookieProcessor:用于处理cookie
- HTTPPasswordMgr:用于管理密码,维护了用户名和密码的表
- HTTPRedirectHandler:用于重定向
- ProxyHandler:用于设置代理,默认代理为空
from urllib.request import ProxyHandler, build_opener from urllib.error import URLError url = "https://www.baidu.com" # 实例化代理 p_handler = ProxyHandler({ "http":"http://39.134.66.14:8080", "https":"https://218.38.52.132:80" }) opener = build_opener(p_handler) # build_opener(实例化的handler处理器) try: res = opener.open(url) html = res.read().decode("utf-8") print(html) except URLError as e: print(e.reason)
- urlopen(url,data,timeoit)
- error:异常处理,定义了由request模块产生的错误
- URLError:error模块的基类,由request模块产生的异常都可以通过这个类捕获,存在reason属性
- HTTPError: URLError的子类,专门处理HTTP请求错误 属性code:返回的HTTP状态码,reason:错误原因 headers:返回请求头
# 一个完整的异常捕获例子 from urllib import error,request try: resp = request.urlopen("http://cuiqingcai.com/index.html") except error.HTTPError as e: print("返回的状态码%s" % e.code) print("返回的错误原因%s" % e.reason) print("返回的请求头%s" % e.headers) except error.URLError as e: print("异常原因%s" % e.reason) else: print("成功")
-parse:解析链接,实现url各部分的抽取、合并、以及链接的转换
- urlparse:实现url的分段和识别,分成六段 scheme://netloc/path;params?query#fragment
- scheme:表示协议
- netloc:表示域名
- path:表示访问路径
- params:代表参数,;后
- query:代表查新条件,?后
- fragment:表示锚点,#后
- urlparse(url, scheme,allow_fragments)
- url:必填,待解析的url
- scheme:默认的协议,假如传入的连接没带协议,会采取这个默认的协议
- allow_fragments:是否忽略锚点部分,值为布尔类型
- urlunparse:根据参数,生成链接,参数为可迭代对象,且长度为6
- urlsplit: 与urlparse一样,参数也形同解析url,分成五段,将params与path部分合并
- urlsplit(url, scheme,allow_fragments)
- urlunsplit: 参数为可迭代对象,且长度为5
- urljoin:生成链接,效果同urlunparse和urlunsplit,但此方法不需要长度
- urljoin(base_url,str) 将两个参数合并对base_url的协议、域名、路径进行补充,base_url其他三个部分则不起作用
- urlencode:将字典对象序列化成符合url参数格式的字符串,一般往url传参中用
- parse_qs:将url中的参数转成字典
- parse_qsl:将url中的参数转化为元组组成的列表
- quote:将数据转化为url格式的编码,一般参数为中文的时候使用
- unquote:将url格式的编码进行解码
```
from urllib import parse
res = parse.urlparse("http:www.baidu.com/index.html;user?id=5#comment")
# 返回结果为元组,res[0],res.scheme
print(res, res[0], res.scheme) # ParseResult(scheme='http', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment')
data = ["http", "www.baidu.com", "index.html", "user", "d=5", "comment"]
res = parse.urlunparse(data)
print(res) # http://www.baidu.com/index.html;user?d=5#comment
res = parse.urlsplit("http:www.baidu.com/index.html;user?id=5#comment")
print(res) # SplitResult(scheme='http', netloc='', path='www.baidu.com/index.html;user', query='id=5', fragment='comment')
data = ["http", "www.baidu.com", "index.html", "d=5", "comment"]
res = parse.urlunsplit(data)
print(res) # http://www.baidu.com/index.html?d=5#comment
res = parse.urljoin("http://www.baidu.com", "index.html")
print(res) # http://www.baidu.com/index.html ,注意合并的规则
data= {
"name":"bob",
"age":18
}
res = parse.urlencode(data, encoding="utf8")
print(res) # name=bob&age=18
res = parse.parse_qs("http:www.baidu.com/index.html;user?id=5&name=3")
print(res) # {'user?id': ['5'], 'name': ['3#comment']}
res = parse.parse_qsl("http:www.baidu.com/index.html;user?id=5&name=3")
print(res) # [('user?id', '5'), ('name', '3')]
q= "张三"
res = "http:www.baidu.com/index.html?q=%s " % parse.quote(q) # 编码
print(res) # http:www.baidu.com/index.html?q=%E5%BC%A0%E4%B8%89
res = parse.unquote(res) # 解码
print(res) # http:www.baidu.com/index.html?q=张三
```
- robotparse:分析网站的robot协议
- robots.txt:网站爬虫协议,全称网络爬虫排除标准,一般存放于网站的根目录下
- 返回的内容:
- user-agent:设置可以爬取网站的爬虫名称
- disallow:设置哪些路径不能爬取
- allow:设置哪些路径可以爬取
# 京东的爬虫协议 # url: https://www.jd.com/robots.txt User-agent: * # 任何爬虫都可以爬 Disallow: /?* Disallow: /pop/*.html Disallow: /pinpai/*.html?* User-agent: EtaoSpider Disallow: / User-agent: HuihuiSpider Disallow: / User-agent: GwdangSpider Disallow: / User-agent: WochachaSpider Disallow: /
- RobotFileParser(url="") # 根据网站的robot协议来判断爬虫是否有权限爬取
- set_url():设置robots.txt文件的连接
- read():读取robots.txt文件并分析,无返回值,必须调用
- parse():解析robots.txt文件
- can_fetch(User-agent, url):返回值为布尔类型,判断此user-agent是否可以爬取此url
- mtime():返回值为上次抓取分析robots的时间
- modified():将当前时间设置为上次抓取的时间
from urllib import robotparser rp = robotparser.RobotFileParser() # 实例化分析类 rp.set_url("https://www.jd.com/robots.txt") # 添加robots地址 rp.read() # 读取robot协议 print(rp.can_fetch("*", "https://www.jd.com/")) # 判断当前爬虫是否可以爬取, True print(rp.mtime()) # 上次抓取的时间 1553685029.0578604 print(rp.modified()) # 设置抓取的时间 none
requests的使用
- 基本用法
- Get请求 requests.get("url", "params", "headers")
- url:请求资源的地址
- params:传入的参数
- headers:请求头,反扒的一种措施
- Post请求 requests.post("url", "params", "headers")
- url:请求资源的地址
- params:传入的参数
- headers:请求头,反扒的一种措施
- 响应属性信息
- text:返回的文本数据
- content:二进制格式的数据,例如音频、视频、图片的
- status_code:返回的状态码
- headers:返回的响应头
- cookies:cookies信息
- url:请求的url信息
- history: 请求历史
import requests # get请求 requests.get(url, params, headers) url = "http://httpbin.org/get" # 请求的url # 请求参数 params = { "name":"Arale", "age":25 } # 请求头 headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36" } # 发送get请求,得到响应 resp = requests.get(url, params=params, headers=headers) print(resp.url) # 打印请求的url 参数在url http://httpbin.org/get?name=Arale&age=25 # post请求 requests.post(url, data, headers) url = "http://httpbin.org/post" # 请求的url # 发送post请求 resp = requests.post(url, data=params, headers=headers) print(resp.url) # 打印请求的url 参数在请求体中 http://httpbin.org/post # 响应属性方法 print(resp.text) # 打印str文本数据 print(resp.content) # 处理二进制数据,例如图片 print(resp.url) # http://httpbin.org/post print(resp.headers) # {'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Origin': '*', 'Content-Encoding': 'gzip', 'Content-Type': 'application/json', 'Date': 'Thu, 28 Mar 2019 01:53:32 GMT', 'Server': 'nginx', 'Content-Length': '343', 'Connection': 'keep-alive'} print(resp.cookies) #
print(resp.status_code) # 200 print(resp.history) # [] - Get请求 requests.get("url", "params", "headers")
- 高级用法
- 上传文件
# 上传文件 files = { "file": open("01_urllib的使用.py", "rb") } resp = requests.post(url, files=files) # 注意key必须为files否则报错 print(resp.text) # 返回内容会多一个files字段
- cookies的使用
- 以为例,写文章的页面需要登陆后才能看见,拿到网站登录后的cookie,再去请求,以达到维持登录状态的效果。
# cookie的使用 headers = { # 此cookie为登录网站后的cookie, "Cookie": "Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1553742403; sajssdk_2015_cross_new_user=1; locale=zh-CN; read_mode=day; default_font=font2; remember_user_token=W1sxMjE1NTM2Ml0sIiQyYSQxMSRmZEdzaHlpLnFsYnZpMG9PbFRQLk91IiwiMTU1Mzc0MjQxMC44MTg2OTc3Il0%3D--48708ad37562cd9a12cfaac066b92cc24e4305d3; _m7e_session_core=167a540dc0e51fd3bb10e0e502e174de; __yadk_uid=8uaAcl2jljk5KfYwGemwVKFoMN89sBuC; Hm_lpvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1553742450; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22169c243959d13-00d4259c8ce7dd-7a1b34-1296000-169c243959e606%22%2C%22%24device_id%22%3A%22169c243959d13-00d4259c8ce7dd-7a1b34-1296000-169c243959e606%22%2C%22props%22%3A%7B%7D%2C%22first_id%22%3A%22%22%7D", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36", "Referer": "https://www.jianshu.com/writer" } url = "https://www.jianshu.com/writer#/notebooks/35136025/notes/43035331" resp = requests.get(url, headers=headers) print(resp.status_code) # 设置cookie,可以访问需要登录才能看见的页面200 # 查看cookie,并可以得到cookie的key value cookie = requests.get("https://www.baidu.com/").cookies print(cookie) for k, v in cookie.items(): print(k + "=" + v)
- session会话维持
- Session():用来维持同一个会话,使用其实例化的对象进行请求
# session:维持会话 session = requests.Session() # 实例化session类,使请求为同一示例,即维持同一会话 session.get("https://www.httpbin.org/cookies/set/arale/123456") resp = session.get("https://www.httpbin.org/cookies") print(resp.text)
- ssl证书认证
- requests中有个参数verify,默认weiTrue,会自动验证网站的证书
# ssl证书的验证,verify参数默认为True try: resp = requests.get("https://inv-veri.chinatax.gov.cn/") print(resp.status_code) # requests.exceptions.SSLError会报错,默认会验证CA证书 except Exception as e: # from requests.packages import urllib3 # urllib3.disable_warnings() # 忽略警告 import logging logging.captureWarnings(True) # 捕获警告到日志的方式忽略警告 resp = requests.get("https://inv-veri.chinatax.gov.cn/", verify=False) # 不去验证证书 print(resp.status_code)
- 代理的设置
# 代理ip的设置 proxies = { "http":"http://47.107.227.104:8888", } resp = requests.get("https://www.baidu.com/", proxies=proxies) print(resp.status_code)
- 超时设置
# 设置超时 try: resp = requests.get("https://www.baidu.com/", timeout=0.001) print(resp.status_code) # requests.exceptions.ConnectTimeout except Exception as e: print(e)
- 身份认证
# 传入auth=(元组) resp = requests.get("url", auth=("user", "pwd")
- prepared requests对象
- 引入Request对象
s = requests.Session() # 实例化session对象 req = requests.Request("get", "https://www.baidu.com/") # 构造请求对象 pre = s.prepare_request(req) # 通过session准备请求数据 resp = s.send(pre) # 发送请求数据,返回响应 print(resp.status_code)
正则表达式
- 常用方法
- match(re, str):从开头开始匹配,如果开头不符合,则匹配失败
- search(re,str):扫描整个字符串,返回第一个符合条件的结果,没有匹配到,则返回none
- findall(re,str):查找整个字符串,返回所有匹配到的结果
- sub(re,要替换的内容,str):替换匹配到的数据
- compile(re):将匹配规则编译成正则表达式对象,以达到复用的效果
- 返回结果为sre_match对象,有两个方法group()\span()
- group():得到结果的内容
- span():得到匹配到的范围,即结果字符串在原字符窜中的位置范围。
- 分组:即字符串截取,用()包裹好想要提取的子字符串,调用group()方法,并传入分组索引即可得到,索引从1开始
- 通用匹配:., . 表示除换行符之外的任意字符,表示匹配前面字符0或多个
- 贪婪与非贪婪:.* 表示贪婪模式,会尽可能的匹配多个导致匹配结果不准确,.?表示非贪婪模式,尽可能匹配更少的,注意:非贪婪模式放在字符串结尾,可能会匹配不到*
- 修饰符:第三个参数,做修饰用
- re.S:表示匹配包括换行符在内的所有字符
- re.I:表示对大小写不敏感
- 转移符: \,用来转义特殊字符
import re
str = "Extra stings Hello 123456 world_This is a Regex Demo Exrea stings"
# + 表示一个或多了
# .*?表示非贪婪模式
# \w{10}匹配字母数字下划线10个,{4,10}贪婪模式,会取10个
# ()分组字符串提取
result = re.match(r"^extra.*?(\d+)\s(\w{10})", str, re.I)
print(result.group()) # 匹配结果的内容
print(result.span()) # 结果字符串在原字符串中的位置范围
print(result.group(1)) # 取出第一个分组结果
print(result.group(2))
爬取猫眼电影
- 使用requests第三方库,请求网站,用正则解析页面,将解析结果写入excel
import requests
import re
import time
import xlwt
# 爬取网页
def get_page(url, headers):
resp = requests.get(url, headers)
return resp.text
# 解析返回的结果
def parse_res(res):
'''
1
9.
5
'''
par = re.compile(r'.*?'
r'(\d+)'
r'.*?data-src="(.*?)"'
r'.*?class="name">(.*?)'
r'.*?class="star">(.*?)'
r'.*?releasetime">(.*?)'
r'.*?class="integer">(.*?)'
r'.*?class="fraction">(.*?)'
r'.*? ', re.S)
items = re.findall(par, res) # 返回list
for item in items:
# 利用生成器返回数据,或者构造元组,存放在list中用return返回
# TODO 返回这样格式是写入excel需要
yield [
item[0],
item[1],
item[2],
item[3].strip()[3:],
item[4][5:],
item[5]+item[6]
]
# 写入文件
def write_to_excel(items):
# 创建一个excel
excel = xlwt.Workbook()
# 添加一个工作区
sheet = excel.add_sheet("电影排名")
# 构造表头信息
head = ["序号", "海报", "名称", "主演", "上映时间", "评分"]
# 将头部信息写入excel表头
for index, value in enumerate(head):
sheet.write(0, index, value)
# 将内容写入excel
for row, item in enumerate(items, 1): # 行数据
for col in range(0, len(item)): # 列数据
sheet.write(row, col, item[col])
excel.save("./猫眼电影排名.xlsx")
# 主程序入口
def main(offset):
url = "https://maoyan.com/board/4?offset=" + str(offset)
headers = {
"User-Agent": '''Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36(KHTML, like Gecko) Chrome
/73.0.3683.75 Safari/537.36'''
}
res = get_page(url, headers) # 请求url
items = parse_res(res) # 解析结果,返回生成器
print(items.__next__()) # python3改为__next__(),之前版本为next()
return items # 返回解析后的结果,生成器
if __name__ == "__main__":
items = []
offset = None
for i in range(0, 10):
item= main(i*10) # 分页爬取
items += list(item) # 将每页结果进行拼接
time.sleep(1) # 每页休眠一秒钟,反扒措施
write_to_excel(items) # 将所有结果一次性写入文件,不一次一次写,因为会覆盖