学习目标:
- 学习urlliib库基本操作
学习内容:
一、urllib库下载源码、图片、视频
爬取百度网页的源代码:
下面展示一些
# 导入所需要的库
import urllib.request
# 目标地址
url="http://www.baidu.com"
# 模拟浏览器向服务器发送请求
response=urllib.request.urlopen(url)
#获取源代码(注意要查看网页的编码格式,如下图解释)
content=response.read().decode("utf-8")
# 打印网页的源代码
print(content)
获取源代码的编码格式,用来解析源码
1. 爬取源码
# -------------------------------目标地址-------------------------------
url_page="http://www.baidu.com"
# -------------------------------模拟浏览器向服务器发送请求将数据下载到本地-------------------------------
urllib.request.urlretrieve(url_page,"baidu.html") #第一个参数表示抓取目标地址 第二个参数表示保存到本地“./baidu.html"文件中 【该段意思为保存百度源码到本地】
# urlretrieve()方法直接将远程数据下载到本地
2. 爬取图片
# --------------------------------下载图片------------------------------
url_img="https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/newxueshuicon-a5314d5c83.png"
urllib.request.urlretrieve(url_img,'xuesu.jpg')
3. 爬取视频
参考链接:https://blog.csdn.net/Angry_Mills/article/details/82705595
以爬取视频目标网页为例子
src属性为 src=“blob:https://open.gaodun.com/b9d3366f-87ef-4328-9d97-31110de519a1”
但是复制打开该链接找不到视频内容 无法进行爬取
分析原因:
网页隐藏了真实的视频链接防止恶意爬取策略
解决方案:
找真实地址,利用抓包工具 打开XHR
XHR的定义
二、urllib定制请求头
对于编写爬虫过程中,有一些网站会有设置反爬机制:对于非浏览器的访问拒绝响应;或短时间频繁爬取会触发网站的反爬机制,导致 ip 被封无法爬取网页。这就需要在爬虫程序中修改请求的 headers 伪装浏览器访问,或者使用代理发起请求。从而绕开网站的反爬机制获取正确的页面。
#------------------------------利用urllib库模拟请求头信息--------------------------------------------
url="https://www.baidu.com"
# 定制我们的请求头信息 模拟成请求是由Chrome浏览器发出
headers={ 'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36'}
# 伪装请求头信息,制造符合服务器要求的请求信息
request=urllib.request.Request(url,headers=headers)
# 给服务器发送请求
response=urllib.request.urlopen(request)
# 将响应信息利用解码格式解码
content=response.read().decode()
print(content)
UA(User Agent) 用户代理,使服务器能够识别客户使用的操作系统以及版本、CPU、浏览器版本、浏览器内核、浏览器渲染引擎、浏览器语言插件等
三、urllib爬GET请求的数据操作
对于有一些网站的请求是用到GET的方式请求
此时如果采用传统的直接使用传统的方法来爬取会报错
# -----------------------------利用urllib库进行GET方式的爬虫-------------------------------------
# 目标地址
url="https://www.runoob.com/python3/python-urllib.html"
# 定制请求头信息
headers={
'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36'
}
# 制定请求信息
request=urllib.request.Request(url=url,headers=headers)
# 给服务器发送请求
response=urllib.request.urlopen(request)
# 解码响应内容
content=response.read().decode('UTF-8')
# 打印请求内容
print(content)
报错内容:
原因分析:
此时网站的编码确实是用utf-8
所以这个原因是网站为了反爬设置的陷阱,他是因为在请求响应中设置了压缩属性,所以让我们得到源码后不能解析出来,参考链接 Python爬取网页Utf-8解码错误及gzip压缩问题的解决办法
解决方案:
先对爬取得到的网站信息进行解压然后再进行解码
整体代码:
# -----------------------------利用urllib库进行GET方式的爬虫-------------------------------------
# 目标地址
url="https://www.runoob.com/python3/python-urllib.html"
# 定制请求头信息
headers={
'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36'
}
# 制定请求信息
request=urllib.request.Request(url=url,headers=headers)
# 给服务器发送请求
response=urllib.request.urlopen(request)
# 解码响应内容
# 获取响应内容
content=response.read()
# 解压并解码响应内容
content=gzip.decompress(content).decode('UTF-8')
# 打印请求内容
print(content)
采用urllib库中的parse对象的quote方法
对于url中有单个中文的编码的话 建议采用quote方法
# --------------------------利用urllib中的quote方法----------------------------------------
# 适用范围,比如目标地址中有编码的问题,比如中文等
# 目标地址 "https://www.baidu.com/s?wd=周杰伦"
url="https://www.baidu.com/s?wd="
# 定制请求头信息 需要 'Accept'因为如果不加上百度服务器会返回百度验证
headers={
'user-agent' : 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_2_6 like Mac OS X) AppleWebKit/604.5.6 (KHTML, like Gecko) Mobile/15D100 QQ/7.5.0.407 V1_IPH_SQ_7.5.0_1_APP_A Pixel/750 Core/UIWebView Device/Apple(iPhone 7) NetType/WIFI QBWebViewType/1',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9'
}
# 需要将url中需要编码的内容进行编码
name=urllib.parse.quote("周杰伦")
# 拼接url
url=url+name
print(url)
# 制定请求信息
request=urllib.request.Request(url=url,headers=headers)
# 给服务器发送请求
response=urllib.request.urlopen(request)
# 获取响应内容
content=response.read().decode("utf-8")
# 打印请求内容
print(content)
采用urllib库中的parse对象的urlencode方法
对于url中有多个参数编码的话 建议采用urlencode方法
# --------------------------利用urllib中的urlencode方法----------------------------------------
# 适用范围,比如目标地址中有多个参数编码的问题,比如中文等
# 目标地址 "https://www.baidu.com/s?wd=周杰伦&sex=男&location=中国台湾省"
base_url="https://www.baidu.com/s?"
# 将url后面的参数用unicode编码整合
data={
"wd":"周杰伦",
"sex":"男",
"location":"中国台湾省"
}
data=urllib.parse.urlencode(data)
print(data)
url=base_url+data
# 定制请求头信息
headers={
'user-agent' : 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_2_6 like Mac OS X) AppleWebKit/604.5.6 (KHTML, like Gecko) Mobile/15D100 QQ/7.5.0.407 V1_IPH_SQ_7.5.0_1_APP_A Pixel/750 Core/UIWebView Device/Apple(iPhone 7) NetType/WIFI QBWebViewType/1',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9'
}
# 需要将url中需要编码的内容进行编码
name=urllib.parse.quote("周杰伦")
# 拼接url
url=url+name
print(url)
# 制定请求信息
request=urllib.request.Request(url=url,headers=headers)
# 给服务器发送请求
response=urllib.request.urlopen(request)
# 获取响应内容
content=response.read().decode("utf-8")
# 打印请求内容
print(content)
四、urllib爬Post请求的数据操作
post请求一般是有数据表单 而且再url中不会显示表单内容,因此请求信息中要编码制作成请求体中
使用案例:
通过以上所学知识,利用urlib库完成爬取翻译网页
注意事项:
要利用抓包工具抓出post请求表单中data数据的key
# -------------------------------------利用urllib库完成对于Post请求的爬取-------------------------------------
# 目标地址
url="https://fanyi.baidu.com/sug"
# 定制请求头
headers={
'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36'
}
# post请求的表单数据
base_data={
"wk":"spider"
}
print(type(base_data)) #<class 'dict'>
# 还得用unicode统一编码
unicode_data=urllib.parse.urlencode(base_data)
print(type(unicode_data))#<class 'str'> 字符串的编码格式默认为utf-8
print("该变量的编码格式是:", unicode_data.encode())
# 还得字符串转成bytes类型
bytes_data=unicode_data.encode("utf-8")
print(type(bytes_data))#<class 'bytes'>
# post请求的数据不能拼接到url中而是要封装成请求体
# 定制请求信息 data的数据使byte类型
request=urllib.request.Request(url=url,data=bytes_data,headers=headers)
# 模拟浏览器向服务器发送请求
response=urllib.request.urlopen(request)
# 获取响应数据内容
content=response.read().decode("utf-8")
print(content)
问题
遇到问题就是会出现未知错误 errno:1000
五、爬取豆瓣电影首页的数据 GET请求案例
1、抓包工具找到抓取请求地址
整体代码:
# ------------------------------爬取豆瓣电影首页信息-------------------------------
# 寻找目标地址
base_url="https://movie.douban.com/j/chart/top_list?type=1&interval_id=100%3A90&action=&start=0&limit=20"
# 伪装UA请求头
headers={
'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36'
}
# 定制请求信息
request=urllib.request.Request(url=base_url,headers=headers)
# 发送请求信息
response=urllib.request.urlopen(request)
# 解码获取响应数据
content=response.read().decode("utf-8")
# 将数据保存到本地 open方法默认的编码格式未GBK 因为内容中有中文所以要制定编码格式未unicode
fp=open('douban.json','w',encoding="utf-8")
fp.write(content)
fp.close()
print(type(content)) #<class 'str'>
print(content)
补充知识内容:
JSON字符串JSON对象以及普通字符串的区别
爬取豆瓣信息的内容为JSON数据中的数组结构
拓展练习一:爬取豆瓣前十页的电影信息
情景:AJAX的get请求
Ajax是什么?Ajax的作用和使用
如何判断网页是AJAX请求
通过判抓包工具中抓的请求头数据为XMLHttpRequest对象
# _*_ coding : utf-8 _*_
# @Time : 2024/4/10 14:09
# @Author : HHL
# @File : 豆瓣前十页电影信息
# @Project : Python基础
# 分析:
# https://movie.douban.com/j/chart/top_list?type=1&interval_id=100%3A90&action=&start=0&limit=20
# https://movie.douban.com/j/chart/top_list?type=1&interval_id=100%3A90&action=&start=20&limit=20
# https://movie.douban.com/j/chart/top_list?type=1&interval_id=100%3A90&action=&start=40&limit=20
# 原理:
# page 1 2 3
# start 0 20 40
# start=(page-1)*20
import urllib.request
import urllib.parse
def get_request(page):
# 确认目标地址
base_url="https://movie.douban.com/j/chart/top_list?type=1&interval_id=100%3A90&action=&"
headers={
'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36'
}
data={
"start":(page-1)*20,
"limit":20
}
data=urllib.parse.urlencode(data)
goal_url=base_url+data
# 定制请求对象 该请求的方式为get所以不需要data参数
request=urllib.request.Request(url=goal_url,headers=headers)
return request
def get_response(request):
response=urllib.request.urlopen(request)
return response
def save_data(content,page):
with open('douban'+str(page)+".json",'w',encoding="utf-8") as fp:
fp.write(content)
if __name__ == '__main__':
start_page=int(input("please enter start page:"))
end_page = int(input("please enter end page:"))
# 串级爬取数据
for page in range(start_page,end_page+1):
# 定制请求对象
request = get_request(page)
# 向服务器发送请求
response=get_response(request)
# 获取响应内容
content=response.read().decode("utf-8")
# 保存到本地
save_data(content,page)
print("爬取第"+str(page)+"页数据成功")
拓展练习二:爬取肯德基官网信息
情景:AJAX的post请求 :
爬取佛山哪些位置有肯德基
为什么data数据需要两次编码
因为第一次(urlencode)是要将data数据unicode为以下第二张view source的格式
第二次编码是需要将datra数据变成二进制bytes类型,因为Request函数中的data必须是bytes类型
# _*_ coding : utf-8 _*_
# @Time : 2024/4/10 23:54
# @Author : HHL
# @File : 爬取肯德基佛山前十页地址信息
# @Project : Python基础
# 分析
# https://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname 第一页
# cname: 佛山
# pid:
# pageIndex: 1
# pageSize: 10
# https://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname 第二页
# cname: 佛山
# pid:
# pageIndex: 2
# pageSize: 10
# https://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname 第三页
# cname: 佛山
# pid:
# pageIndex: 3
# pageSize: 10
import urllib.request
import urllib.parse
def get_request(page):
# 目标地址
base_url = "https://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname"
# 请求头伪装
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36'
}
# 请求体数据
data = {
'cname': '佛山',
'pid': '',
'pageIndex': page,
'pageSize': '10'
}
# post请求中要为data数据编码两次
encode_data = urllib.parse.urlencode(data)
bytes_data = encode_data.encode("utf-8") # 目的是转化为bytes类型给Request函数用
# 请求对象定制
request = urllib.request.Request(url=base_url, headers=headers, data=bytes_data)
return request
def get_response(request):
# 给服务器发送请求
response = urllib.request.urlopen(request)
return response
def get_content(response):
content = response.read().decode("utf-8")
return content
def save_data(content, page):
with open("KFCLocation" + str(page) + ".json", "w", encoding='utf-8') as fp:
fp.write(content)
print("爬取肯德基第" + str(page) + "页数据成功")
if __name__ == '__main__':
start_page = int(input("please enter start page:"))
end_page = int(input("please enter end page:"))
for page in range(start_page, end_page + 1):
# 定制请求对象
request = get_request(page)
# 向服务器中发送请求得到响应数据
response = get_response(request)
# 获取响应内容
content = get_content(response)
# 将数据保存到本地中
save_data(content, page)