1.urllib庫
1.1 基本使用
使用urllib來擷取百度首頁的源碼
import urllib.request
# 1、定義一個url 就是你要通路的位址
url = 'http://www.baidu.com'
# 2、模拟浏覽器向伺服器發送請求 response響應
response = urllib.request.urlopen(url)
# 3、擷取響應中的頁面的源碼
content = response.read().decode('utf-8')
# 4、列印資料
print(content)
read方法,傳回的是位元組形式的二進制資料,我們要将二進制的資料轉換為字元串,需解碼 : decode(‘編碼的格式’)
1.2 1個類型和6個方法
import urllib.request
url = 'http://www.baidu.com'
# 模拟浏覽器向伺服器發送請求
response = urllib.request.urlopen(url)
# 一個類型:response是HTTPResponse的類型
print(type(response))
# 按照一個位元組一個位元組的去讀
content = response.read()
print(content)
# 傳回多少個位元組
content = response.read(5)
print(content)
# 讀取一行
content = response.readline()
print(content)
# 一行一行讀取 直至結束
content = response.readlines()
print(content)
# 傳回狀态碼 如果是200了 那麼就證明我們的邏輯沒有錯
print(response.getcode())
# 傳回的是url位址
print(response.geturl())
# 擷取是一個狀态資訊
print(response.getheaders())
一個類型:HTTPResponse
六個方法: read、readline、readlines、getcode、geturl、getheaders
1.3 下載下傳
import urllib.request
# 下載下傳網頁
url_page = 'http://www.baidu.com'
# url代表的是下載下傳的路徑 filename檔案的名字
urllib.request.urlretrieve(url_page,'baidu.html')
# 下載下傳圖檔
url_img = 'https://img1.baidu.com/it/u=3004965690,4089234593&fm=26&fmt=auto&gp=0.jpg'
urllib.request.urlretrieve(url= url_img,filename='lisa.jpg')
# 下載下傳視訊
url_video = 'https://vd3.bdstatic.com/mda-mhkku4ndaka5etk3/1080p/cae_h264/1629557146541497769/mda-mhkku4ndaka5etk3.mp4?v_from_s=hkapp-haokan-tucheng&auth_key=1629687514-0-0-7ed57ed7d1168bb1f06d18a4ea214300&bcevod_channel=searchbox_feed&pd=1&pt=3&abtest='
urllib.request.urlretrieve(url_video,'hxekyyds.mp4')
在python中,可以寫變量的名字,也可以直接寫值
1.4 請求對象的定制
import urllib.request
url = 'https://www.baidu.com'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}
# 因為urlopen方法中不能存儲字典 是以headers不能傳遞進去
# 請求對象的定制
request = urllib.request.Request(url=url,headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf8')
print(content)
1.5 get請求的quote方法
get請求參數,如果是中文,需要對中文進行編碼,如下面這樣,如果不編碼會報錯。
需求 擷取 https://www.baidu.com/s?wd=周傑倫的網頁源碼
編碼後如下: https://www.baidu.com/s?wd=%E5%91%A8%E6%9D%B0%E4%BC%A6
import urllib.request
import urllib.parse
url = 'https://www.baidu.com/s?wd='
# 請求對象的定制為了解決反爬的第一種手段
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}
# 将周傑倫三個字變成unicode編碼的格式,需要依賴于urllib.parse
name = urllib.parse.quote('周傑倫')
# 将轉碼後的字元串拼接到路徑後面
url = url + name
# 請求對象的定制
request = urllib.request.Request(url=url,headers=headers)
# 模拟浏覽器向伺服器發送請求
response = urllib.request.urlopen(request)
# 擷取響應的内容
content = response.read().decode('utf-8')
# 列印資料
print(content)
quote适用于将中文轉碼成Unicode編碼
1.6 get請求的urlencode方法
urlencode應用場景:多個參數的時候。如下
https://www.baidu.com/s?wd=周傑倫&sex=男
# 擷取https://www.baidu.com/s?wd=%E5%91%A8%E6%9D%B0%E4%BC%A6&sex=%E7%94%B7的網頁源碼
import urllib.request
import urllib.parse
base_url = 'https://www.baidu.com/s?'
data = {
'wd':'周傑倫',
'sex':'男',
'location':'中國台灣省'
}
new_data = urllib.parse.urlencode(data)
# 請求資源路徑
url = base_url + new_data
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}
# 請求對象的定制
request = urllib.request.Request(url=url,headers=headers)
# 模拟浏覽器向伺服器發送請求
response = urllib.request.urlopen(request)
# 擷取網頁源碼的資料
content = response.read().decode('utf-8')
# 列印資料
print(content)
1.7 post請求
import urllib.request
import urllib.parse
url = 'https://fanyi.baidu.com/sug'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}
data = {
'kw':'spider'
}
# post請求的參數,必須要進行編碼
data = urllib.parse.urlencode(data).encode('utf-8')
request = urllib.request.Request(url=url,data=data,headers=headers)
# 模拟浏覽器向伺服器發送請求
response = urllib.request.urlopen(request)
# 擷取響應的資料
content = response.read().decode('utf-8')
# 字元串--》json對象
import json
obj = json.loads(content)
print(obj)
post請求的參數 必須要進行編碼:data = urllib.parse.urlencode(data)
編碼之後 必須調用encode方法 : data = urllib.parse.urlencode(data).encode(‘utf-8’)
post的請求的參數,是不會拼接在url的後面的 ,而是需要放在請求對象定制的參數中:
request = urllib.request.Request(url=url,data=data,headers=headers)
1.8 異常
import urllib.request
import urllib.error
url = 'http://www.doudan1.com'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}
try:
request = urllib.request.Request(url = url, headers = headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)
except urllib.error.HTTPError:
print('系統正在更新。。。')
except urllib.error.URLError:
print('我都說了 系統正在更新。。。')
1.9 handler
為什麼要學習handler?
- urllib.request.urlopen(url) 不能定制請求頭
- urllib.request.Request(url,headers,data) 可以定制請求頭
- Handler:定制更進階的請求頭(随着業務邏輯的複雜 請求對象的定制已經滿足不了我們的需求,動态cookie和代理不能使用請求對象的定制)
# 需求 使用handler來通路百度 擷取網頁源碼
import urllib.request
url = 'http://www.baidu.com'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}
request = urllib.request.Request(url = url,headers = headers)
# handler build_opener open
#(1)擷取hanlder對象
handler = urllib.request.HTTPHandler()
#(2)擷取opener對象
opener = urllib.request.build_opener(handler)
# (3) 調用open方法
response = opener.open(request)
content = response.read().decode('utf-8')
print(content)
1.10 代理
為什麼需要代理?因為有的網站是禁止爬蟲的,如果用真實的ip去爬蟲,容易被封掉。
import urllib.request
url = 'http://www.baidu.com/s?wd=ip'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}
# 請求對象的定制
request = urllib.request.Request(url = url,headers= headers)
# 模拟浏覽器通路伺服器
# response = urllib.request.urlopen(request)
proxies = {
'http':'118.24.219.151:16817'
}
# handler build_opener open
handler = urllib.request.ProxyHandler(proxies = proxies)
opener = urllib.request.build_opener(handler)
response = opener.open(request)
# 擷取響應的資訊
content = response.read().decode('utf-8')
# 儲存
with open('daili.html','w',encoding='utf-8')as fp:
fp.write(content)
代理可以使用:快代理。可以使用代理池來代替一個代理
2.解析技術
2.1 xpath
xpath安裝及加載
1.安裝lxml庫
pip install lxml ‐i https://pypi.douban.com/simple
2.導入lxml.etree
from lxml import etree
3.etree.parse() 解析本地檔案
html_tree = etree.parse(‘XX.html’)
4.etree.HTML() 伺服器響應檔案
html_tree = etree.HTML(response.read().decode(‘utf‐8’)
5.解析擷取DOM元素
html_tree.xpath(xpath路徑)
按照xpath的chrome插件,使用 ctrl + shift + x 打開插件
xpath基本文法
1.路徑查詢
//:查找所有子孫節點,不考慮層級關系
/ :找直接子節點
2.謂詞查詢
//div[@id]
//div[@id=“maincontent”]
3.屬性查詢
//@class
4.模糊查詢
//div[contains(@id, “he”)]
//div[starts‐with(@id, “he”)]
5.内容查詢
//div/h1/text()
6.邏輯運算
//div[@id=“head” and @class=“s_down”]
//title | //price
示例:
from lxml import etree
# xpath解析本地檔案
tree = etree.parse('test.html')
# 查找ul下面的li
li_list = tree.xpath('//body/ul/li')
# 查找所有有id的屬性的li标簽,text()擷取标簽中的内容
li_list = tree.xpath('//ul/li[@id]/text()')
# 找到id為l1的li标簽 注意引号的問題
li_list = tree.xpath('//ul/li[@id="l1"]/text()')
# 查找到id為l1的li标簽的class的屬性值
li = tree.xpath('//ul/li[@id="l1"]/@class')
# 查詢id中包含l的li标簽
li_list = tree.xpath('//ul/li[contains(@id,"l")]/text()')
# 查詢id的值以l開頭的li标簽
li_list = tree.xpath('//ul/li[starts-with(@id,"c")]/text()')
#查詢id為l1和class為c1的
li_list = tree.xpath('//ul/li[@id="l1" and @class="c1"]/text()')
li_list = tree.xpath('//ul/li[@id="l1"]/text() | //ul/li[@id="l2"]/text()')
2.2 JsonPath
JsonPath隻能解析本地檔案。
jsonpath的安裝及使用
pip安裝:
pip install jsonpath
jsonpath的使用:
obj = json.load(open(‘json檔案’, ‘r’, encoding=‘utf‐8’))
ret = jsonpath.jsonpath(obj, ‘jsonpath文法’)
示例:
{
"store": {
"book": [
{
"category": "修真",
"author": "六道",
"title": "壞蛋是怎樣練成的",
"price": 8.95
},
{
"category": "修真",
"author": "天蠶洋芋",
"title": "鬥破蒼穹",
"price": 12.99
},
{
"category": "修真",
"author": "唐家三少",
"title": "鬥羅大陸",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "修真",
"author": "南派三叔",
"title": "星辰變",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"author": "老馬",
"color": "黑色",
"price": 19.95
}
}
}
解析上面的json資料,具體文法,參考如下部落格:
https://blog.csdn.net/luxideyao/article/details/77802389
import json
import jsonpath
obj = json.load(open('jsonpath.json','r',encoding='utf-8'))
# 書店所有書的作者
author_list = jsonpath.jsonpath(obj,'$.store.book[*].author')
# 所有的作者
author_list = jsonpath.jsonpath(obj,'$..author')
# store下面的所有的元素
tag_list = jsonpath.jsonpath(obj,'$.store.*')
# store裡面所有東西的price
price_list = jsonpath.jsonpath(obj,'$.store..price')
# 第三個書
book = jsonpath.jsonpath(obj,'$..book[2]')
# 最後一本書
book = jsonpath.jsonpath(obj,'$..book[(@.length-1)]')
# 前面的兩本書
book_list = jsonpath.jsonpath(obj,'$..book[0,1]')
book_list = jsonpath.jsonpath(obj,'$..book[:2]')
# 條件過濾需要在()的前面添加一個?
# 過濾出所有的包含isbn的書。
book_list = jsonpath.jsonpath(obj,'$..book[?(@.isbn)]')
# 哪本書超過了10塊錢
book_list = jsonpath.jsonpath(obj,'$..book[?(@.price>10)]')
2.3 BeautifulSoup
基本介紹
- BeautifulSoup簡稱:bs4
- 什麼是BeatifulSoup? BeautifulSoup,和lxml一樣,是一個html的解析器,主要功能也是解析和提取資料
-
優缺點
缺點:效率沒有lxml的效率高
優點:接口設計人性化,使用友善
安裝以及建立
- 安裝
pip install bs4 -i https://pypi.douban.com/simple
- 導入
from bs4 import BeautifulSoup
- 建立對象
- 伺服器響應的檔案生成對象
soup = BeautifulSoup(response.read().decode(), ‘lxml’)
- 本地檔案生成對象
soup = BeautifulSoup(open(‘1.html’), ‘lxml’)
- 伺服器響應的檔案生成對象
注意:預設打開檔案的編碼格式gbk是以需要指定打開編碼格式
節點定位
1.根據标簽名查找節點
soup.a # 隻能找到第一個a
soup.a.name
soup.a.attrs
2.函數
- find(傳回一個對象)
find(‘a’):隻找到第一個a标簽
find(‘a’, title=‘名字’)
find(‘a’, class_=‘名字’)
- find_all(傳回一個清單)
find_all(‘a’) :查找到所有的a
find_all([‘a’, ‘span’]) 傳回所有的a和span
find_all(‘a’, limit=2) 隻找前兩個a
- select(根據選擇器得到節點對象)【☆☆☆】
-
:pelement
-
:.firstname.class
-
:#firstname#id
-
:屬性選擇器
:li = soup.select(‘li[class]’)[attribute]
:li = soup.select(‘li[class=“hengheng1”]’)[attribute=value]
-
層級選擇器
:
div p 後代選擇器
div>p 子代選擇器:某标簽的第一級子标簽
div,p div或p标簽的所有的對象
-
節點資訊
- 擷取節點内容:适用于标簽中嵌套标簽的結構
obj.string
obj.get_text()【推薦】
- 節點的屬性
tag.name:擷取标簽名
tag.attrs:将屬性值作為一個字典傳回
- 擷取節點屬性
obj.attrs.get(‘title’)【常用】
obj.get(‘title’)
obj[‘title’]
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<ul>
<li id="l1">張三</li>
<li id="l2">李四</li>
<li>王五</li>
<a href="" id="" class="a1">尚矽谷</a>
<span>嘿嘿嘿</span>
</ul>
</div>
<a href="" title="a2">百度</a>
<div id="d1">
<span>
哈哈哈
</span>
</div>
<p id="p1" class="p1">呵呵呵</p>
</body>
</html>
使用BeautifulSoup解析上面的html
from bs4 import BeautifulSoup
# 預設打開的檔案的編碼格式是gbk,是以在打開檔案的時候需要指定編碼
soup = BeautifulSoup(open('bs4的基本使用.html',encoding='utf-8'),'lxml')
# 根據标簽名查找節點,找到的是第一個符合條件的資料
print(soup.a)
# 擷取标簽的屬性和屬性值
print(soup.a.attrs)
# bs4的一些函數
# (1)find:傳回的是第一個符合條件的資料
print(soup.find('a'))
# 根據title的值來找到對應的标簽對象
print(soup.find('a',title="a2"))
# 根據class的值來找到對應的标簽對象 注意的是class需要添加下劃線
print(soup.find('a',class_="a1"))
# (2)find_all 傳回的是一個清單,并且傳回了所有的a标簽
print(soup.find_all('a'))
# 如果想擷取的是多個标簽的資料 那麼需要在find_all的參數中添加的是清單的資料
print(soup.find_all(['a','span']))
# limit的作用是查找前幾個資料
print(soup.find_all('li',limit=2))
# (3)select(推薦)
# select方法傳回的是一個清單,并且會傳回多個資料
print(soup.select('a'))
# 可以通過.代表class 我們把這種操作叫做類選擇器
print(soup.select('.a1'))
print(soup.select('#l1'))
# 屬性選擇器:通過屬性來尋找對應的标簽
# 查找到li标簽中有id的标簽
print(soup.select('li[id]'))
# 查找到li标簽中id為l2的标簽
print(soup.select('li[id="l2"]'))
# 層級選擇器
# 後代選擇器:找到的是div下面的li
print(soup.select('div li'))
# 子代選擇器:某标簽的第一級子标簽
print(soup.select('div > ul > li'))
# 找到a标簽和li标簽的所有的對象
print(soup.select('a,li'))
# 擷取節點内容
obj = soup.select('#d1')[0]
# 如果标簽對象中,隻有内容,那麼string和get_text()都可以使用
# 如果标簽對象中,除了内容還有标簽,那麼string就擷取不到資料 而get_text()是可以擷取資料
# 推薦使用get_text()
print(obj.string)
print(obj.get_text())
# 節點的屬性
obj = soup.select('#p1')[0]
# name是标簽的名字
print(obj.name)
# 将屬性值左右一個字典傳回
print(obj.attrs)
# 擷取節點的屬性
print(obj.attrs.get('class'))
print(obj.get('class'))
print(obj['class'])