做平臺網(wǎng)站外包多少錢啊常見的網(wǎng)絡(luò)營銷方式有哪幾種
爬蟲基礎(chǔ)
文章目錄
- 爬蟲基礎(chǔ)
- 爬蟲簡介
- 爬蟲的用途
- 爬蟲的合法性
- 規(guī)避風(fēng)險
- 看懂協(xié)議
- 反爬機(jī)制
- 反反爬策略
- 認(rèn)識HTTP
- HTTP協(xié)議--HyperText Transfer Protocol
- HTML--HyperText Markup Language
- HTTPS
- 如何查看網(wǎng)站是什么協(xié)議呢
- 使用端口號
- URL組成部分詳解
- 常用的請求- Request Method
- 常見的請求頭參數(shù)
- User-Agent 瀏覽器名
- Referer
- Cookie 身份證
- 常見的響應(yīng)狀態(tài)碼 - Response Code
- 分析網(wǎng)頁
- 靜態(tài)網(wǎng)頁 - 源代碼
- 動態(tài)網(wǎng)站(異步加載) - 抓包
- 抓包工具
- Elements
- Network
- Requests----get
- 基于www.hao123.com做一個網(wǎng)頁采集器
- Requests---post
- 基于百度翻譯,實(shí)現(xiàn)文本翻譯
- 基于豆瓣電影,獲取華語評分前十電影(失敗,原因:該產(chǎn)品程序員對網(wǎng)頁做出了調(diào)整,跟著課做不出來)
- 數(shù)據(jù)解析
- 正則表達(dá)式
- 匹配單個字符
- 匹配多個字符
- 示例一 ------以中間部分內(nèi)容
- 示例二 ------以開頭^
- 示例三 ----以結(jié)尾判定$
- 貪婪匹配與非貪婪匹配*?+
- 正則表達(dá)式的應(yīng)用案例
- 匹配手機(jī)號 1[3-9]\d{9}
- 匹配郵箱 \w+@[0-9a-z]+.[a-z]+
- 匹配網(wǎng)址 (https:|http:\ftp:)//\S+
- 匹配身份證號 \d{17}[xX\d]
- 正則表達(dá)式re模塊
- 最低級的字符串處理函數(shù)match
- 中級字符串處理函數(shù)search
- 高級字符串處理函數(shù)findall 也是常用的函數(shù)
- 字符串替換函數(shù)sub
- 案例 小說篩選器
- 字符串分割函數(shù)split
- 換行加注釋
- 正則表達(dá)式預(yù)處理compile
- 正則表達(dá)式的轉(zhuǎn)義字符與原生字符
- 案例 美元$與正則表達(dá)式取結(jié)尾\$
- 案例 以反斜杠作為標(biāo)識符
- 案例 匹配網(wǎng)頁的標(biāo)題
- 正則表達(dá)式xPath模塊(XML Path Language)
- xPath節(jié)點(diǎn)
- xPath語法
- 謂語
- 選取未知節(jié)點(diǎn)
- 選取若干路徑
- 數(shù)據(jù)解析
- xPath-lxml庫的使用
- lxml庫
- lxml庫-讀取本地xml文件
- 使用HTML的方式讀入文件
- 定位元素
- 按標(biāo)簽順序定位元素
- 按照包含的元素定位
- /與//的區(qū)別
- python案例---下載人生格言
- python案例---下載美女圖片
- 案例總結(jié)
- BS4--beautifulsoup4庫
- BS4語法
- 對象的種類
- 獲取標(biāo)簽
- 獲取標(biāo)簽的名稱
- 獲取標(biāo)簽的屬性/屬性值
- 改變標(biāo)簽的屬性值
- 獲取標(biāo)簽的文本內(nèi)容
- 獲取注釋部分的內(nèi)容
- 遍歷文檔樹
- 搜索文檔樹
- CSS選擇器
爬蟲簡介
就是通過代碼,模擬瀏覽器上網(wǎng),獲取互聯(lián)網(wǎng)的數(shù)據(jù)過程
爬蟲的用途
- 爬取網(wǎng)頁上的文字、圖片、視頻、音頻等
- 自動填寫表單,打卡等
爬蟲的合法性
- 在法律中不被禁止
- 具有一定風(fēng)險
- 不能干擾被訪問網(wǎng)站的正常運(yùn)營----不能高并發(fā)式訪問網(wǎng)站,就是不能1秒內(nèi)向網(wǎng)上發(fā)送上萬次請求,可能導(dǎo)致服務(wù)器崩潰
- 不能抓取受法律保護(hù)的特定信息和數(shù)據(jù)
規(guī)避風(fēng)險
- 盡量不高并發(fā)訪問
- 設(shè)計敏感內(nèi)容時及時停止爬取或傳播
- 閱讀robots.txt協(xié)議,這上面會說那些數(shù)據(jù)不能爬取
- 如何找一個網(wǎng)站的這個協(xié)議呢
- 只需要在那個網(wǎng)址后面加上斜杠和這個名稱就好了
- 比如https://www.bilibili.com/robots.txt
- 就可以看到哪些數(shù)據(jù)不該訪問
- 如何找一個網(wǎng)站的這個協(xié)議呢
看懂協(xié)議
user-agent 用戶代理
Baiduspider 百度爬蟲
Disallow 禁止訪問一切內(nèi)容
反爬機(jī)制
通過技術(shù)手段防止爬蟲程序?qū)W(wǎng)站數(shù)據(jù)進(jìn)行爬取
- 對疑似爬蟲的ip進(jìn)行限制
反反爬策略
破解反爬機(jī)制,獲取相關(guān)數(shù)據(jù)
- 定時切換ip,繼續(xù)獲取數(shù)據(jù)
認(rèn)識HTTP
HTTP協(xié)議–HyperText Transfer Protocol
意思就是超文本發(fā)送和接收協(xié)議?,F(xiàn)在可以傳輸二進(jìn)制碼流,也就是音頻圖片等等
HTML–HyperText Markup Language
這是一個頁面,HTTP所要呈現(xiàn)的頁面,服務(wù)器端口是80、
一些小網(wǎng)站啥的,瀏覽器會顯示不安全
HTTPS
是加密版的HTTP,比HTTP多一個SSL層,服務(wù)器端口是443。因?yàn)槭羌用苓^的所以更安全
比如京東啥的,瀏覽器會顯示一個鎖,在網(wǎng)址前面
如何查看網(wǎng)站是什么協(xié)議呢
只需要看看網(wǎng)址前綴是什么
使用端口號
在瀏覽器中的網(wǎng)址后面,加上冒號,再加上80或者443,就能顯示頁面了
URL組成部分詳解
URL是Uniform Resource Locator的簡寫,統(tǒng)一資源定位符。
一個URL由以下幾部分組,對于 scheme://host:port/path/?query-string=xxx#anchor 來說
scheme:代表的是訪問的協(xié)議,一般為http或者h(yuǎn)ttps以及ftp等
? 就是網(wǎng)址前面的http啥的
host:主機(jī)名,域名,比如www.baidu.com
? 就是網(wǎng)址后面緊挨著的域名
port:端口號。當(dāng)你訪問一個網(wǎng)站的時候,瀏覽器默認(rèn)使用80端口
? 這個端口號瀏覽器默認(rèn)添加,一般看不見,放在域名后面,與域名連在一起比如說www.baidu.com:80
path:查找路徑。比如:sz.58.com/chuzu/ 后面的/chuzu就是path
? 這個是你點(diǎn)開初始頁面的某個項目,這個例子是點(diǎn)開了58租房的一個出租的頁面,可以把初始頁面類比成一個大文件夾,然后你點(diǎn)開哪一個頁面就是點(diǎn)開哪一個小文件夾
? 有時候,這個類似文件夾的路徑是虛擬的
query-string:查詢字符串,比如:www.baidu.com/s?wd=python,后面的wd=python就是查詢字符串
? 這個wd后面的python就是用戶想要搜索的信息,比如去qq音樂里面搜一個歌手,你在搜索框里面填寫歌手名,實(shí)際上是寫在了wd的后面,然后后端再根據(jù)信息返回給你
anchor:錨點(diǎn),前端用來做頁面定位的?,F(xiàn)在一些前后端分離項目,也用錨點(diǎn)來做
? 因?yàn)橐粋€頁面有很多板塊,為了使頁面更美觀,我們會把一些信息分類,一類一類的去展示,這些板塊標(biāo)上號,就可以排版了,比如#1是目錄,#2是歌手大全等等
常用的請求- Request Method
就是你打開一個網(wǎng)站時,你向服務(wù)器發(fā)送請求的方式,常用的有兩種,一個是GET 一個是POST
一共是8種請求方式,其他6種與爬蟲關(guān)系不大
一般來說,GET請求,我們不會向服務(wù)器發(fā)送數(shù)據(jù),不會占用服務(wù)器的資源,幾乎沒影響
而POST請求是需要讓服務(wù)器特意接收我們的信息,給我們預(yù)留空間,會對服務(wù)器產(chǎn)生一定的影響
這種都是我們要登錄了,給服務(wù)器提醒,或者給服務(wù)器上傳數(shù)據(jù)
出來這種信息,需要我們登錄一下,就會出現(xiàn)這個文件了
常見的請求頭參數(shù)
在http協(xié)議中,向服務(wù)器發(fā)送請求時,數(shù)據(jù)分為3種
第一種是放在鏈接url里面,
第二種是把數(shù)據(jù)放在參數(shù)體body里面,在post請求中有一部分就在body里面
第三個就是把數(shù)據(jù)放在請求頭里面
User-Agent 瀏覽器名
就是服務(wù)器想知道你這個請求是哪個瀏覽器發(fā)送的
這里面一般有你請求的瀏覽器版本信息,還有你電腦配置的信息
我們以后寫爬蟲時,不填寫這個信息,人家就會默認(rèn)你是Python,人家后臺用最簡單的判斷就能實(shí)現(xiàn)反爬技術(shù),人家就會給你一些假數(shù)據(jù)或者直接拒絕你的請求
為了防止這樣,我們要模擬自己是一個瀏覽器,自己填寫User-Agent,所以要經(jīng)常設(shè)置這個值,來偽裝我們的爬蟲
Referer
表明當(dāng)前請求是從哪個url過來的,意思就是,我們的想要獲取的頁面,是從哪里來訪問的
比如,我現(xiàn)在想登錄淘寶網(wǎng),那么登錄界面就應(yīng)該從主頁點(diǎn)擊而來
這里的referer表明我們是從京東主頁過來的,也就是從京東主頁發(fā)送請求而來到這個頁面
有時候,人家也會檢查你的referer來判斷你來到當(dāng)前頁面是否合法,如果不合法就給你反爬
應(yīng)對這個反爬機(jī)制,就需要我們自己指定一下referer,找指定頁面
Cookie 身份證
一般是我們填寫賬號和密碼后,服務(wù)器會給你一個特定的cookie,
服務(wù)器通過檢查你的cookie來判斷你的身份,
有cookie之后,再訪問服務(wù)器就不需要登錄了,就是身份證
一些反爬較強(qiáng)的網(wǎng)站會檢測你是否有cookie,登錄或者不登錄都會檢查cookie
沒用cookie就會拒絕訪問
這些參數(shù)如何設(shè)置,請聽下回分曉
常見的響應(yīng)狀態(tài)碼 - Response Code
就是我們向服務(wù)器發(fā)送請求,服務(wù)器會返回一個狀態(tài)響應(yīng)碼
返回
-
200:請求正常,服務(wù)器正常的返回數(shù)據(jù)。
-
301:永久重定向。比如在訪問www.jingdong.com的時候會重定向到www.id.com.
- 就是你訪問的是舊網(wǎng)址,人家不用了,給你指向新的地址
-
302臨時重定向:比如在訪問一個需要登錄的頁面的時候,而此時沒有登錄,那么就會重定向到登錄頁面。
- 就像知乎一樣,知道你沒登錄就強(qiáng)制跳轉(zhuǎn)到登錄頁面,哪怕你再訪問知乎主頁,還是會被傳回登錄頁面,這個傳送就是臨時重定向
-
400/404:請求的url在服務(wù)器上找不到。換句話說就是請求鏈接url錯誤。
-
403:服務(wù)器拒絕訪問,權(quán)限不夠,或者是被反爬了
-
500:服務(wù)器內(nèi)部錯誤。可能是服務(wù)器出現(xiàn)bug了,或者宕機(jī)了。 這個與咱們無關(guān)
分析網(wǎng)頁
網(wǎng)頁分為兩類:靜態(tài)網(wǎng)頁和動態(tài)網(wǎng)頁
靜態(tài)網(wǎng)頁 - 源代碼
就是計算機(jī)向服務(wù)器發(fā)送請求,服務(wù)器一次性把所有內(nèi)容打包發(fā)送給我們
像是4399啊或者一些工具網(wǎng)站啊,就是那種一次性展示所有的固定信息的網(wǎng)站
我們可以選擇源代碼的方式獲取數(shù)據(jù)
動態(tài)網(wǎng)站(異步加載) - 抓包
就是計算機(jī)向服務(wù)器請求數(shù)據(jù),服務(wù)器第一次只給框架,等計算機(jī)接收完之后再次請求,服務(wù)器才把真正的數(shù)據(jù)發(fā)送過來。然后計算機(jī)開始填充內(nèi)容,進(jìn)行拼接
相當(dāng)于一次請求分了好幾步才給你整個頁面
這種方式,源代碼中一般只有框架,不是我們想要的,所以我們通過抓包的形式,去找數(shù)據(jù)包
找到的數(shù)據(jù)包,是json文件,需要進(jìn)一步拆分
像這種信息比較全的,就是服務(wù)器第二次向?yàn)g覽器發(fā)送的數(shù)據(jù)包了
然后就是瀏覽器自己拼接了
抓包工具
Elements
幫助分析網(wǎng)頁結(jié)構(gòu),有的數(shù)據(jù)是ajax請求的,也就是二次請求,就是異步加載的數(shù)據(jù)包,不在源代碼里面
Network
查看整個網(wǎng)頁發(fā)送的所以網(wǎng)絡(luò)請求
Requests----get
獲取靜態(tài)網(wǎng)頁并保存在本地文件中
用瀏覽器打開這個文件,就可以訪問這個網(wǎng)頁了
基于www.hao123.com做一個網(wǎng)頁采集器
下面是作業(yè):基于www.hao123.com做一個網(wǎng)頁采集器
# https://www.baidu.com/s? 這個網(wǎng)址問號?后面的都是數(shù)據(jù)了
# tn=50000021_hao_pg
# &ie=utf-8&sc=UWd1pgw-pA7EnHc1FMfqnHTzPH64PjTYn1TzPiuW5y99U1Dznzu9m1Ykn1TvnW04njms
# &ssl_sample=normal
# &srcqid=8885165236642971965
# &H123Tmp=nunew7
# &word=%E5%91%A8%E6%9D%B0%E4%BC%A6# tn: 50000021_hao_pg
# ie: utf-8
# sc: UWd1pgw-pA7EnHc1FMfqnHTzPH64PjTYn1TzPiuW5y99U1Dznzu9m1Ykn1TvnW04njms
# ssl_sample: normal
# srcqid: 8885165236642971965
# H123Tmp: nunew7
# word: 周杰倫
上面是自己先搜索任意一個關(guān)鍵字,得到的網(wǎng)址
然后打開network找包頭,就是這些data數(shù)據(jù)tn,ie等等,然后發(fā)現(xiàn)word就是我們填充關(guān)鍵字的地方import requests 導(dǎo)入這個模塊keyword = input('請輸入要查詢的內(nèi)容:') 索取關(guān)鍵字
url = f'https://www.baidu.com/s?word={keyword}' 只需要給個網(wǎng)站域名www..com,再給個關(guān)鍵字就好了qingqiu_header = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0'} 這是從自己的瀏覽器復(fù)制過來的請求頭,
這里要把請求頭以字典(鍵值對)的格式賦值給我們的請求頭results = requests.get(url=url,headers = qingqiu_header ) 通過查看瀏覽器的network任意一個文件發(fā)現(xiàn)是get的獲取方式,就用get發(fā)送請求,參數(shù)把我們的網(wǎng)址填進(jìn)去,然后再把請求頭填進(jìn)去,模擬瀏覽器請求
如果不寫請求頭,將返回亂碼print(results) -- 200 正常訪問
print(results.url) -- 查看我們輸入的網(wǎng)址 https://www.baidu.com/s?word=%E5%91%A8%E6%9D%B0%E4%BC%A6
print(results.request) ---返回請求的類型,比如get/post
print(results.request.headers) --- 查看請求頭,也就是user-agent,如果我們填寫了請求頭,這個結(jié)果和請求頭是一樣的如果沒有填寫請求頭,就會返回默認(rèn)值{'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}發(fā)現(xiàn)默認(rèn)是python,直接反爬,讓我們得到亂碼print(results.text) 這里是顯示一下你獲取的數(shù)據(jù),以text的格式with open(keyword + '.html','w',encoding='utf-8') as f: 這里是把數(shù)據(jù)儲存在本地文件里面,你用瀏覽器可以打開這個文件f.write(results.text)print(f'已下載成功……{keyword}') 告訴用戶,你已經(jīng)成功把網(wǎng)頁保存起來了
步驟:
- 搜索任意關(guān)鍵詞
- 獲取網(wǎng)址 www…com
- 找請求頭user-agent
- 找關(guān)鍵詞對應(yīng)的接口
- 把關(guān)鍵詞和網(wǎng)址以及請求頭作為參數(shù)使用requests模塊
- 檢查狀態(tài)響應(yīng)碼是不是200
- 以text的格式查看返回的內(nèi)容是真實(shí)有效
- 以只寫的形式創(chuàng)建文件,把網(wǎng)頁寫進(jìn)去,就保存好了
Requests—post
首先知道什么是異步獲取,判斷是post請求
然后點(diǎn)開負(fù)載,也就是data數(shù)據(jù)
不難發(fā)現(xiàn),只有一個數(shù)據(jù),這個數(shù)據(jù)就是我們輸入的文本
也就是以后我們填寫關(guān)鍵詞的地方
然后就是篩選數(shù)據(jù)
這是在data值下的第0位元素中v的值
基于百度翻譯,實(shí)現(xiàn)文本翻譯
# https://www.hao123.com/
import pprint 格式化模塊
import requests 請求模塊keyword = input('請輸入要查詢的單詞:')
url = 'https://fanyi.baidu.com/sug'headersss = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0'}data = {'kw':f'{keyword}'}ret = requests.post(url=url,headers=headersss,data=data) 就比get多了個data參數(shù)# pprint.pprint(ret.json()) 先用這里找數(shù)據(jù)ret_json = ret.json() 超文本轉(zhuǎn)json字典格式
print(str(ret_json['data'][0]['v'])) 最后打印找到的數(shù)據(jù)
基于豆瓣電影,獲取華語評分前十電影(失敗,原因:該產(chǎn)品程序員對網(wǎng)頁做出了調(diào)整,跟著課做不出來)
數(shù)據(jù)解析
- 正則表達(dá)式
- xPath
- BS4
正則表達(dá)式
概念: 根據(jù)某種方法從字符串中找到想要匹配的數(shù)據(jù) , 就是給你篇文章讓你找個詞,圈出來這個詞在哪些地方
優(yōu)點(diǎn):速度快,準(zhǔn)確度高
缺點(diǎn):新手入門難度大
匹配單個字符
- 單個字符點(diǎn) ” . “ ,可以匹配任意單字符 1,2,3,4,5,6,7,我,@,空格
- 兩個點(diǎn) ” … “ ,可以兩兩匹配字符,就是 12,34,56,78
- ” \d “,可以匹配單個數(shù)字,就是值匹配字符串里面的數(shù)字
- ” \d\d “,就是匹配兩個相鄰的數(shù)字,比如,相約1980年,匹配19,80 幾個斜杠d,就是匹配幾個數(shù)字
- ” \D “,匹配非數(shù)字內(nèi)容,單個字符
- ” \w “,匹配數(shù)字,字母和下劃線
- ” \W “,就是跟小w取反,非數(shù)字非字母非下劃線
- ” \s “,匹配空格、換行符、制表符等等看不見的東西
- “ \S ”,取反
- “ [] ”,組合匹配,將文本與方括號內(nèi)的的任意字符匹配,只要有一個字符能匹配上就行
- [a-z] 匹配所有的小寫字母
- [a-zA-Z] 匹配所有的大小寫字母
- [a-zA-Z0-9] 匹配所有的字母和數(shù)字
- [a-zA-Z0-9_@$] 匹配括號里面全部的內(nèi)容,字母數(shù)字和你輸入的特殊符號
- 理論上這個匹配規(guī)則可以取代上面所有的"\什么東西"
匹配多個字符
示例一 ------以中間部分內(nèi)容
電話:123456
電話:1122334455
電話:123
電話:
- 電話:\d 匹配前三行到數(shù)字1的位置
- 電話:\d* 匹配全部,數(shù)字可以是0個
- 電話:\d+ 匹配前三個,至少一個數(shù)字的
- 電話:\d? 匹配0或者1個
- 電話:\d{6} 匹配前兩個,至少6位數(shù)電話的前6位
- 電話:\d{6,} 匹配前兩個,至少6位數(shù)的電話
- 電話:\d{6,8} 匹配前兩個,位數(shù)是6到8的電話
示例二 ------以開頭^
112233
123aaa
ab123
- ^\d 匹配以數(shù)字開頭的字符串 , 前兩個的第一位數(shù)字
- ^\d+ 以數(shù)字開頭的字符串,只匹配數(shù)字部分,第一個的全部,第二個的數(shù)字部分
示例三 ----以結(jié)尾判定$
123.@qq.com
www.222.com
666.cn
-
com$ 匹配前兩個的com部分
-
.*com$ 匹配前兩個全部
-
在 Python 的正則表達(dá)式中,
.*
是一個常用的模式,具體含義如下:.
: 匹配任意字符(除了換行符\n
)。*
: 表示匹配前面的字符(在這里是.
)0 次或多次。
因此,
.*
的整體含義是:匹配任意數(shù)量的任意字符,包括零個字符,直到遇到不符合匹配條件的字符(如果有的話)。
-
-
^www.*com$ 組合一下,就是以www開頭,以com結(jié)尾的第二個了
貪婪匹配與非貪婪匹配*?+
對于123456來說
- \d* 默認(rèn)盡量匹配最多,最少是0個,也就是123456
- \d*? 按照*最少的數(shù)量匹配,也就是不匹配
- \d+ 至少匹配一個,默認(rèn)是盡可能多,也就是123456
- \d+? 也就是按照+的最少數(shù)量匹配,1,2,3,4,5,6
對于網(wǎng)頁的源代碼來說,比如
<h1>這是標(biāo)題<h1>
- <.*> 匹配全部<h1>這是標(biāo)題<h1> 貪婪模式
- <.*?> 匹配<h1> 非貪婪模式
正則表達(dá)式的應(yīng)用案例
匹配手機(jī)號 1[3-9]\d{9}
首先是數(shù)字1開頭
第二位一般是3到9的其中一個
然后是剩下的9個數(shù)字
匹配郵箱 \w+@[0-9a-z]+.[a-z]+
對于112233@168.com來說
使用\w+ 貪婪匹配字符串,這里就分割成了112233,168,com三個字符串
再加上一個@,成為\w+@,限定字符串是以@結(jié)尾的
接著就是域名了,比如QQ郵箱就是@qq.com,還有@168.com等等,一般是數(shù)字和小寫字母[0-9a-z]
再跟著一個+,就是貪婪匹配數(shù)組和小寫字母的字符串的最大長度,
最后是那個com,用小寫字母匹配,再貪婪一下,就好了
匹配網(wǎng)址 (https:|http:\ftp:)//\S+
首先是常見的網(wǎng)址開頭,有三個https和http以及ftp
因?yàn)檫@三個頭都有可能,所以想要全部匹配,需要用到圓括號括起來,再用一個豎“|”表示或,就可以了
去除開頭剩下的內(nèi)容不為空就可以了,用\S,貪婪一下
匹配身份證號 \d{17}[xX\d]
一般是17位數(shù)字,加上最后一位,可能是x也可能是數(shù)字
正則表達(dá)式re模塊
一個負(fù)責(zé)處理字符串內(nèi)容的模塊,也就是網(wǎng)站中超多的源代碼,超文本等等
這也是python內(nèi)置的模塊,不需要下載,直接導(dǎo)入就可以了
因?yàn)橐谡齽t表達(dá)式中頻繁使用轉(zhuǎn)義字符,在python解釋器中更期望兩個反斜杠的用法
比如,在字符串中使用反斜杠要注意轉(zhuǎn)義的問題,這里使用\d{4}會報錯,使用\\d{4}就沒事了
最低級的字符串處理函數(shù)match
這個函數(shù)只能完全匹配字符串才能提取內(nèi)容
這個函數(shù)的返回值不能直接使用,需要配合group函數(shù),才能把match識別的信息提取出來
把需要提取的信息用正則表達(dá)式括起來,group(1)對應(yīng)的就是第一個括號提取的內(nèi)容,group(2)對應(yīng)的就是第二個括號了,剩下的一次類推
還有一種方法可以一次性提取所有括號的內(nèi)容,也就是groups(),這個函數(shù)會把提取的內(nèi)容放到一個元組里面,可以以數(shù)組的形式打印出來
text = '姓名:小明 出生日期:2000年1月1日 畢業(yè)年齡:2024年6月1日'# match 函數(shù)只能從起始位置匹配整個字符串,最麻煩的一種,一旦“姓名”改成“1姓名”就識別不出來了t1 = re.match('姓名',text)
print(t1)
# 如果直接打印match的返回值,就會顯示所有內(nèi)容,如,<re.Match object; span=(0, 3), match='姓名'>
# 如果這個字符串里面沒找到macth第一個參數(shù)的字符串,就會返回“NoneType”
# 這里還要進(jìn)一步篩選,使用group函數(shù),返回match識別的內(nèi)容
print(t1.group()) # 姓名# 這里可以用正則表達(dá)式,對字符串進(jìn)行劃分
# 如
t2 = re.match('姓名:.*出生日期:\\d{4}',text)
print(t2.group()) # 姓名:小明 出生日期:2000t2 = re.match('姓名:.*出生日期:\\d{4}.*畢業(yè)年齡:\\d{4}',text)
print(t2.group()) # 姓名:小明 出生日期:2000年1月1日 畢業(yè)年齡:2024t2 = re.match('姓名:.*出生日期:(\\d{4}).*畢業(yè)年齡:(\\d{4}).*',text)
print(t2.group(1)) # 給想要提取的信息加上圓括號,再使用group函數(shù),從左往右,從1開始,打印匹配的內(nèi)容
# t2.group(1) 就是2000
# t2.group(2) 就是2024
print(t2.groups()) # 也可以把這些內(nèi)容以元組的形式打印出來 ('2000', '2024')
print(t2.groups()[0]) # 這里就是打印元組的第0個元素 2000
中級字符串處理函數(shù)search
這個函數(shù)比較高級,不需要完全匹配字符串,它可以自己去找對應(yīng)的位置,如果找不到則返回NoneType
這些函數(shù)找不到對應(yīng)內(nèi)容基本上都是返回這個語句NoneType
這個函數(shù),只能返回第一個匹配的內(nèi)容,意思就是處理多個相同內(nèi)容時,這個語句只能返回一次內(nèi)容
text2 = '姓名:小明 出生日期:2000年1月1日 畢業(yè)年齡:2024年6月1日'
t1 = re.search('出生日期:(\\d{4}).*年齡:(\\d{4})',text2)
print(t1.group(1),t1.group(2),t1.groups())# search中使用groups也是放到元組里面
# 掃描整個字符串返回第一個與之匹配的內(nèi)容,意思就是處理多個相同內(nèi)容時,這個語句只能返回一次內(nèi)容
高級字符串處理函數(shù)findall 也是常用的函數(shù)
這個函數(shù)可以一次性掃描整個字符串,并把匹配的內(nèi)容打印出來,如果有多個匹配的內(nèi)容就以列表的形式打印出來
# re.findall 直接打印會放到一個列表里面,想要提取出來,只需要和使用數(shù)組一樣,加個方括號就好了
text3 = '姓名:小明 出生日期:2000年1月1日 畢業(yè)年齡:2024年6月1日'
t3 = re.findall('出生日期:(\\d{4}).*年齡:(\\d{4})',text3) # 如果要識別的內(nèi)容有多個,就會先放到列表里面,再放到元組里面,想要提取內(nèi)容需要再使用方括號
print(t3) [('2000', '2024')]text4 = """
姓名:小明 出生日期:2000年1月1日 畢業(yè)年齡:2024年6月1日
姓名:小李 出生日期:2001年2月1日 畢業(yè)年齡:2024年7月1日
姓名:小張 出生日期:2002年2月2日 畢業(yè)年齡:2024年8月1日
"""t4 = re.findall('出生日期:(\\d{4}).*年齡:(\\d{4})',text4)
print(t4) # 對于多個相同內(nèi)容的字符串就會把一行里面的內(nèi)容放到一個元組里面去,有幾組內(nèi)容就有幾個元組 [('2000', '2024'), ('2001', '2024'), ('2002', '2024')]t5 = re.findall('出生日期:(.*?) 畢業(yè)',text4) # 使用非貪婪模式,避免匹配的內(nèi)容不理想
print(t5) ['2000年1月1日', '2001年2月1日', '2002年2月2日']
當(dāng)然現(xiàn)在的編譯器比較智能了,這個貪婪與非貪婪有的時候沒啥區(qū)別,為了嚴(yán)謹(jǐn)一些,還是自己區(qū)分一下吧
字符串替換函數(shù)sub
這個的使用原理和replace函數(shù)是一樣的,但是sub可以使用正則表達(dá)式,在處理問題的時候更加靈活
第一個參數(shù)是正則表達(dá)式,第二個是要替換的內(nèi)容,第三個是參考文本
不會改變原來的內(nèi)容,只是在原來的基礎(chǔ)上更改后保存在新的變量中
# re.sub() 替換函數(shù)
text5 = '0566-12345678'
t6 = re.sub('^\\d{4}','0333',text5) # 第一個參數(shù)是正則表達(dá)式,第二個是要替換的內(nèi)容,第三個是參考文本
print(t6,text5) # 并不會對原來的文本進(jìn)行替換,只是把替換后的內(nèi)容放到新變量里面text6 = '0566-123123123'.replace('0566','0333')
print(text6) # 他們的效果一樣,但是sub更靈活,當(dāng)內(nèi)容不一樣時,還是正則表達(dá)式通用
案例 小說篩選器
text = """
<div class="noveContent">
<p>1945年四月,山城。</p>
<p>宋孝安,果黨軍統(tǒng)內(nèi)勤處第三科科長,中校軍銜,因?yàn)樾欧鹩謽O為擅長布局、算無遺策,被稱之為‘軍統(tǒng)小諸葛’。</p>
<p>此時他神情緊張,聽著戴老板辦公室時不時傳出來的呵斥聲,讓他額頭上的汗珠沒斷過。</p>
<p>手上拿著佛珠,不停地轉(zhuǎn)動著:“明誠長官,你可把我害慘了啊。”</p>
<p>此時他不祈禱能夠升官發(fā)財,只求不被槍斃。</p>
<p>戴笠,軍事委員會調(diào)查統(tǒng)計局副局長,軍銜少將,實(shí)際上是軍統(tǒng)的實(shí)際控制人,此時他非常生氣,看著眼前的陳漢文,一手叉腰一手拿著手絹指著對方:“誰讓你跑到滬上殺人的?76號總部被你炸上了天,李士群死了,丁默邨被你刺成重傷,如果不是他運(yùn)氣好,現(xiàn)在也被你送上天了,你想怎么樣?我有沒有和你說過,我們和76號不是敵人,在陜北的共黨才是,還有新四軍……”</p>
<p>陳漢文坐在沙發(fā)上,旁邊戴笠的訓(xùn)斥就當(dāng)沒聽到,看到水燒開后,他端起來開始泡茶。</p>
<p>陳漢文,字明誠,少將銜,軍統(tǒng)總務(wù)處處長。</p>
"""
t7 = re.sub('<.*?>',' ',text)
print(t7) # 完成了字符串的替換,這里是把網(wǎng)頁的格式字符全部替換為空
字符串分割函數(shù)split
第一個參數(shù)是想要分割的標(biāo)識符,像是逗號句號或者空格
第二個參數(shù)是需要處理的文本
# re.split() 字符串分割 按照設(shè)計的正則表達(dá)式的格式,把字符串分割后放在列表里面
text4 = '你好 python 工程師'
t9 = re.split(' ',text4)
print(t9) # ['你好', 'python', '工程師']text4 = '你好,python.工程師'
t9 = re.split('[,.]',text4) # 使用正則表達(dá)式更靈活 上面的那個也可以是t9 = re.split('[ ]',text4)
print(t9) # ['你好', 'python', '工程師']
換行加注釋
注意在多行語句組合一句話的時候,需要用三個”“”雙引號“”“
# 換行加注釋
text2 = '姓名:小明 出生日期:2000年1月1日 畢業(yè)年齡:2024年6月1日'
t10 = re.findall("""
出生日期:(\\d{4}) # 現(xiàn)在就可以添加注釋了
.*年齡:(\\d{4}) # 比如解釋說明,這里提取的是畢業(yè)年齡
""",text2,re.VERBOSE) # re.VERBOSE 可以忽略#和換行符以及中間的內(nèi)容
print(t10)
findall的第三個參數(shù)re.VERBOSE,意思是告訴程序,在正則表達(dá)式里面換行了,而且可能還有注釋
re.VERBOSE
標(biāo)志通常與re
模塊中的re.compile()
、re.search()
、re.match()
、re.finditer()
等函數(shù)一起使用。- 這樣可以很方便地寫出復(fù)雜的正則表達(dá)式,同時在其中添加注釋和換行,使其更加清晰。
正則表達(dá)式預(yù)處理compile
正則表達(dá)式的預(yù)處理,因?yàn)閷τ谧址奶幚砜赡芨哌_(dá)上萬次,那么我們就需要考慮一下預(yù)處理
把正則表達(dá)式提前處理成被執(zhí)行的格式,如果不預(yù)處理,每次遇到字符串都要重新編譯,預(yù)處理提高效率
# 預(yù)編譯re.compile
text5 = '姓名:小明 出生日期:2000年1月1日 畢業(yè)年齡:2024年6月1日'
tt = re.compile('出生日期:(\\d{4}).*年齡:(\\d{4})') # 正則表達(dá)式預(yù)處理格式放在tt里面
t11 = re.findall(tt,text5) # 按照tt的格式,對text5處理
print(t11)
正則表達(dá)式的轉(zhuǎn)義字符與原生字符
對于一個語句中,如果含有轉(zhuǎn)義字符,比如“\d”,“\n”等等
我們想要直接打印就會出現(xiàn)問題
text = 'hello\nihao\didi'
print(text)
如果你是在python解釋器pycharm中,復(fù)制這段話,就會發(fā)現(xiàn)"\n"跟其他的字符不太一樣
你嘗試打印一下
hello
ihao\didi
就是這種結(jié)果,可能程序還會報錯,因?yàn)閈d是字符串插入數(shù)字的標(biāo)識符
\n 沒了
因?yàn)閈n有自己的意思,是換行符,也是在字符串中出現(xiàn),
現(xiàn)在你不想轉(zhuǎn)義,就想打印hello\nihao\didi這句話
-
要么在已有的反斜杠前面或者后面再加一個反斜杠,組成兩個反斜杠
-
要么就是在字符串前面加一個小寫的r,比如,text = r’hello\nihao\didi’
- 這個小寫的r原型是raw意思是原生,就是告訴程序別轉(zhuǎn)義
案例 美元$與正則表達(dá)式取結(jié)尾$
This apple is available for $5 and $7
如果直接放在正則表達(dá)式里面,想要提取5美元和7美元這個價格
很明顯我們需要的是$這個標(biāo)識符,還有"\d+"
解決方式有兩個:
- ”\$\d+“
- r"$\d+"
案例 以反斜杠作為標(biāo)識符
abc123\water
在這里,我們想要提取反斜杠前面的123這個數(shù)值
需要用到反斜杠作為標(biāo)識符,我們想要的是”(\d+)\“
可惜的是,你使用字符串函數(shù),并把這個內(nèi)容作為正則表達(dá)式,會報錯
第二個反斜杠會和后面的引號結(jié)合,此時你想再用一個反斜杠,很抱歉又錯了,在程序預(yù)編譯的時候會處理掉一個反斜杠
程序又會把這個反斜杠和引號結(jié)合
所以只能再加兩個反斜杠,也就是4個反斜杠在預(yù)編譯的時候,每兩個反斜杠消去一個,最后真正執(zhí)行的時候,還剩兩個,程序就能識別了
text = 'hello\water'
t1 = re.findall("(\d+)\\\\",text)
print(text)
上面這些話證明了使用反斜杠來轉(zhuǎn)轉(zhuǎn)義字符很麻煩,我們只需要
t1 = re.findall(r"(\d+)\\",text)
使用r固定這個字符串,不讓程序在預(yù)處理的時候消去反斜杠就可以了,
案例 匹配網(wǎng)頁的標(biāo)題
首先,拿到兩個源代碼,分析一下,發(fā)現(xiàn)標(biāo)題從=后面開始,A站前面結(jié)束
我們直接放到正則表達(dá)式里面,然后把想要提取的部分弄成(.*?)
text1 = '<meta name="keywords" content="【AC獨(dú)家】千星緒~又誰在月牙下做一曲新謠,舞蹈·偶像,宅舞,楚殅南w,A站,AcFun,ACG,彈幕">'
text2 = '<meta name="keywords" content="戴眼鏡的可以嗎......(今日開心視頻:1541),娛樂,搞笑,表情笑笑坊,A站,AcFun,ACG,彈幕">'
t1 = re.findall('<meta name="keywords" content="(.*?),A站,AcFun,ACG,彈幕">',text1)
t2 = re.findall('<meta name="keywords" content="(.*?),A站,AcFun,ACG,彈幕">',text2)
print(t1,t2)
這個案例是想要講帶有表情的網(wǎng)址,比如B站的 嗶哩嗶哩 (゜-゜)つロ 干杯~-bilibili這個表情
有時候也會在正則表達(dá)式里面出現(xiàn),如果正則表達(dá)式里面有這些括號什么的,也需要反轉(zhuǎn)義字符
正則表達(dá)式xPath模塊(XML Path Language)
這個模塊主要是處理網(wǎng)頁(XML和HTML)源代碼里面的內(nèi)容,因?yàn)榇蟛糠值男畔⒍际欠旁谠创a的元素和屬性中
像是字符串、圖片、音頻等等
xPath節(jié)點(diǎn)
在源代碼中有七種類型的節(jié)點(diǎn):
- 元素
- 屬性
- 文本
- 命名空間
- 處理指令
- 注釋
- 文檔(根)節(jié)點(diǎn)
XML文檔被xPath看成節(jié)點(diǎn)樹,也是從根出發(fā),用Tab對齊來控制子節(jié)點(diǎn)父節(jié)點(diǎn),這些節(jié)點(diǎn)之間的關(guān)系
xPath語法
表達(dá)式 | 描述 |
---|---|
nodename | 直接寫這個節(jié)點(diǎn)的名字,就是選它所有的子節(jié)點(diǎn) |
/ | 一個斜杠,從根節(jié)點(diǎn)選取 |
// | 兩個斜杠,就是從整個源代碼中任意位置匹配節(jié)點(diǎn)(相對簡單一些,不用找頭節(jié)點(diǎn)) |
. | 一個點(diǎn),意味著選取當(dāng)前節(jié)點(diǎn) |
… | 兩個點(diǎn),選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) |
@ | 選取屬性 |
路徑表達(dá)式 | 結(jié)果 |
---|---|
bookstore | 選取 bookstore 元素的所有子節(jié)點(diǎn)。 |
/bookstore | 選取根元素 bookstore。 注釋:假如路徑起始于正斜杠(/),則此路徑始終代表到某元素的絕對路徑! |
bookstore/book | 選取屬于 bookstore 的子元素的所有 book 元素 |
//book | 選取所有 book子元素,而不管它們在文檔中的位置 |
bookstore//book | 選擇屬于 bookstore 元素的后代的所有 book 元素,而不管它們位于 bookstore 之下的什么位置。 |
//@lang | 選取名為lang 的所有屬性。 |
謂語
謂語用來查找某個特定的節(jié)點(diǎn)或者包含某個指定的值的節(jié)點(diǎn)。謂語被嵌在方括號中。
路徑表達(dá)式 | 結(jié)果 |
---|---|
/bookstore/book[1] | 選取屬于 bookstore 子元素的第一個 book 元素。 |
/bookstore/book[last()] | 選取屬于 bookstore 子元素的最后一個 book 元素。 |
/bookstore/book[last()-1] | 選取屬于 bookstore 子元素的倒數(shù)第二個 book 元素。 |
/bookstore/book[position()??] | 選取最前面的兩個屬于bookstore 元素的子元素的book元素 |
//title[@lang] | 選取所有擁有名為lang 的屬性的 title 元素。 |
//title[@lang=‘eng’] | 選取所有 title 元素,且這些元素?fù)碛兄禐閑ng的lang 屬性 |
/bookstore/book[price>35.00] | 選取 bookstore元素的所有book元素,且其中的price 元素的值須大于35.00。 |
/bookstore/book[price>35.00]/title | 選取bookstore元素中的book元素的所有title 元素,且其中的 price 元素的值須大于 35.00。 |
選取未知節(jié)點(diǎn)
通配符 | 描述 |
---|---|
/bookstore/* | 選取bookstore元素的所有子元素 |
@* | 選取文檔所有元素 |
//title[@*] | 選取所有帶有屬性title的元素 |
選取若干路徑
使用“|”可以一次訪問多個對象
路徑表達(dá)式 | 結(jié)果 |
---|---|
//book/title|//book/price | 選取book元素的所有title和price元素 |
//title|//price | 選取文檔中所有title和price元素 |
/bookstore/book/title|//price | 選取屬于bookstore元素的book元素的所有title元素,以及整個文檔中所有的price元素 |
數(shù)據(jù)解析
xPath-lxml庫的使用
lxml庫
- Ixml 是 一個HIML/XML的解析器,主要的功能是解析和提取HTML/XML 數(shù)據(jù)
- Ixml和正則一樣,也是用C語言實(shí)現(xiàn)的,是一款高性能的 Python HIML/XMI解析器,我們可以利用之前學(xué)習(xí)的XPath語法,來快速的定位特定元素以及節(jié)點(diǎn)信息。
- lxml python 官方文檔:http://lxml.de/index.html
lxml庫-讀取本地xml文件
- 我們可以利用他來解析IIML代碼,并且在解析HIML代碼的時候,如果HIML代碼不規(guī)范,他會自動的進(jìn)行補(bǔ)全。
- from lxml import etree
- 這是導(dǎo)入模塊
- xml= etree.parse(xPath test1.html’)
- 這里是把本地文件讀入xml或者h(yuǎn)tml,直接打印是一顆etree樹
- parse對文檔的語法極為嚴(yán)格,少一點(diǎn)就會報錯
- print(etree.tostring(xml).decode('unicode ))
- etree.tostring(xml) 這里是把樹拿出來了,但是以字節(jié)byte的形式打開的
- 所以還要etree.tostring(xml).decode( )再加一個函數(shù),意思是以字符串的形式打開
使用HTML的方式讀入文件
使用HTML讀文件的時候,對文件比較友好,可以拋棄parse了
定位元素
-
定位“xPath練習(xí)”
/head/title/text()
意思是找到節(jié)點(diǎn)head再找title節(jié)點(diǎn),找到title節(jié)點(diǎn)之后再訪問title節(jié)點(diǎn)里面的文本信息
-
定位”百里守約“
/body/div/p/text()
-
定位”李清照“
/body//div[@class=“song”]//p/text()
定位body節(jié)點(diǎn),找屬性值位class=“song”的div節(jié)點(diǎn),再訪問下面的所有p節(jié)點(diǎn),打印p節(jié)點(diǎn)的文本部分
有多個對象的時候,會得到一個列表,這里的李清照排在第一位,可以使用列表的形式得到信息
假如,我們用對象result來接收這個列表,打印李清照就是result[0]
如果是倒數(shù)第3個也可以是result[-3]
-
定位屬性”song“
這里是獲取節(jié)點(diǎn)的屬性值
有時候,一些信息是包在屬性里面,我們就需要得到他們
//body//div/@class
這個操作就是,先找body節(jié)點(diǎn),下面的div節(jié)點(diǎn)的屬性class的值,也就是等號后面引號包起來的東西
按標(biāo)簽順序定位元素
這里的李清照是第一個p節(jié)點(diǎn)
可以使用,//body//div[@class=“song”]/p[1]/text() (從1開始)
last元素,指最后一個標(biāo)簽,比如上面的最后一個是蘇軾,那么p[last]就是蘇軾
按照包含的元素定位
上面的圖片中,在body節(jié)點(diǎn)下面有兩個div標(biāo)簽,這里的第二個div標(biāo)簽還包含一個a節(jié)點(diǎn)
所以每個div標(biāo)簽還能根據(jù)包含的子節(jié)點(diǎn)來定位,定位第一個div,//div[p]
定位第二個div節(jié)點(diǎn),//div[p and a] ,意思就是這個div下面有兩個子節(jié)點(diǎn)a和p
/與//的區(qū)別
單個/,意思就是從當(dāng)前位置查找,就是你需要先找到一個確定的節(jié)點(diǎn),才能使用/
單/,不太好用,需要寫的特別具體,就像文件操作里面的絕對路徑一樣,需要從盤開始定位
雙//,就是從任意位置查找,這里我們一般給了屬性值,就只能找到一個節(jié)點(diǎn),也就是我們想要的東西,這里就是文件操作的相對路徑,寫的東西比較少
python案例—下載人生格言
python項目實(shí)戰(zhàn)-xPath下載人生格言-CSDN博客
python案例—下載美女圖片
python項目實(shí)戰(zhàn)——下載美女圖片-CSDN博客
案例總結(jié)
寫完這兩個案例之后,感覺使用xPath語法提取內(nèi)容,特別簡單
就是確定內(nèi)容都在源代碼里面,把這個源代碼轉(zhuǎn)換成etree樹,以節(jié)點(diǎn)樹的方式,去獲取信息
然后就是固定的幾步:
- 使用requests模塊得到源代碼
- 使用etree.HTML函數(shù),把源代碼轉(zhuǎn)換成節(jié)點(diǎn)樹
- 使用xPath語法提取內(nèi)容
掌握了這個語法之后,感覺一些比較基礎(chǔ)的網(wǎng)址的任何內(nèi)容都可以爬取了
比如,我為了查看自己在CSDN上發(fā)表的博客的瀏覽量,寫了python實(shí)現(xiàn)csdn文章瀏覽量日志-CSDN博客
覺得只是查看,沒意思,就加了一個獲取時間的庫,寫成日志,記錄我每次查看的時間和得到的數(shù)據(jù),
這里還能改一改,比如,把查看的博客名稱也記錄在本地文件中,格式就是“博客名稱+瀏覽量+記錄時間”
回頭看看,之前那個豆瓣電影,也能爬取了,,,暫且先不做了,繼續(xù)往下開搞!
BS4–beautifulsoup4庫
和lxml庫一樣,都是對HTML/XML的解釋器,主要功能也是提取數(shù)據(jù)
lxml是局部遍歷,beautiful soup是基于HTMl DOM(document object model),也就是整個文件解析DOM樹
因?yàn)槭菍θ績?nèi)容解析,所以性能要更低一點(diǎn)
BS4語法
導(dǎo)入庫
from bs4 import BeautifulSoup
實(shí)例化
soup = BeautifulSoup(html,‘html.parser’) 第一個參數(shù)是html的文檔,第二個是解析方式
解析方式有兩種:html.parser和lxml,區(qū)別就是對字符的補(bǔ)全方式不同
一般使用第一個解析方式
soup庫自帶一個整理格式的函數(shù),soup.prettify()這個函數(shù)就跟之前的pprint一樣,但是這個是以html的格式顯示
對象的種類
- Tag:
Tag 通俗點(diǎn)講就是 HTML 中的一個個標(biāo)簽。我們可以利用 soup 加標(biāo)簽名輕松地獲取這些標(biāo)簽的內(nèi)容,這些對象的類型是bs4.element.tag。但是注意,它查找的是在所有內(nèi)容中的第一個符合要求的標(biāo)簽。 - NavigableString:
如果拿到標(biāo)簽后,還想獲取標(biāo)簽中的內(nèi)容。那么可以通過tag.string獲取標(biāo)簽中的文字。 - BeautifulSoup:
BeautifulSoup 對象表示的是一個文檔的全部內(nèi)容,大部分時候,可以把它當(dāng)作Tag 對象,它支持 遍歷文檔樹 和 搜索文檔樹 中描述的大部分的方法. - Comment:
Tag,NavigableString,BeautifulSoup 幾乎覆蓋了html和xml中的所有內(nèi)容,但是還有一些特殊對象,容易讓人擔(dān)心的內(nèi)容是文檔的注釋部分Comment 對象是一個特殊類型的NavigableString 對象
這個Conmment就是針對注釋部分的類型
獲取標(biāo)簽
print(soup.a) 就可以拿到整個a標(biāo)簽的全部內(nèi)容,包括屬性和文本部分
soup.a的類型就是bs4.element.Tag
獲取標(biāo)簽的名稱
print(soup.a.name) 打印出來就是a
獲取標(biāo)簽的屬性/屬性值
print(soup.a.attrs) 打印出來的就是一個字典,包含是所有的屬性{’屬性名‘:’屬性值‘,’屬性名‘:’屬性值‘,’屬性名‘:’屬性值‘}
想要打印具體的屬性值
print(soup.a[‘href’]) 打印的就是a標(biāo)簽的href屬性的值
還有另一種方式,print(soup.a.get(‘href’)) 和上面那個一樣
改變標(biāo)簽的屬性值
soup.a[‘href’] = ‘hello’
print(soup.a)
獲取標(biāo)簽的文本內(nèi)容
print(soup.a.string) 就相當(dāng)于lxml里面的//a/text()
print(soup.a.text)
print(soup.a.get_text())
這三個都是一樣的效果,他們的type都是bs4.element.NavigableString
獲取注釋部分的內(nèi)容
<b>
? <!–Hey,How are you?–>
<b>
上面這個以!感嘆號開頭的標(biāo)簽是一個注釋
# 導(dǎo)入庫
from bs4.element import Comment
print(soup.b.string) # Hey,How are you?
print(ytype(soup.b.string)) # 類型是bs4.element.Comment
遍歷文檔樹
- contents和children :
contents:返回所有子節(jié)點(diǎn)的列表 就是以列表的形式打印節(jié)點(diǎn)
children:返回所有子節(jié)點(diǎn)的迭代器 打印子節(jié)點(diǎn),默認(rèn)不顯示換行符,可以使用repr的方式顯示出來 - strings 和 stripped_.strings
strings:如果tag中包含多個字符串 ,可以使用 .strings 來循環(huán)獲取
stripped_strings:輸出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白內(nèi)容
body_tag = soup.body
print(body_tag.contents) 打印列表
for tag in body_tag.children: 循環(huán)得到子節(jié)點(diǎn)print(tag) 打印子節(jié)點(diǎn)print(repr(tag)) 顯示換行符的方式打印子節(jié)點(diǎn) ,與上一條語句有一個就行
for tag in body_tag.strings:print(tag)
# 直接打印,不顯示換行符,有很多空白
# 使用repr函數(shù),顯示換行符,減少空白
# 但是不想要換行符,也不想要空白
for tag in body_tag.stripped_.strings:print(tag)# 可以去除換行符也沒有空行
搜索文檔樹
- find和find_all方法:
搜索文檔樹,一般用得比較多的就是兩個方法,一個是find,一個是find_all。
find方法是找到第一個滿足條件的標(biāo)簽后就立即返回,只返回一個元素。
find_all方法是把所有滿足條件的標(biāo)簽都選到,然后返回。
- find
tr = soup.find('tr')
print(tr)
# 會把第一個tr標(biāo)簽包含的所有內(nèi)容全部打印出來
-
find_all
trs = soup.findall('tr') print(trs) # 返回的是一個列表,包含的是所有的tr標(biāo)簽 # 使用for循環(huán)可以打印出來 for tr in trs:print(tr)print('--'*30) # 分隔符
- 獲取id='nr’的標(biāo)簽,有兩種寫法
- 直接寫屬性
trs = soup.find_all('tr',id='nr')
for tr in trs:print(tr)print('**'*30)
在獲取tr標(biāo)簽的基礎(chǔ)上,多了一個條件id=nr
- 以字典的形式把屬性以鍵值對的形式填入
trs = soup.find_all('tr',attrs={'id':'nr'})
for tr in trs:print(tr)print('**'*30)
他們的效果是一樣的
- 獲取有兩個特定屬性的標(biāo)簽
tds = soup.find_all('td',class_="odd",align="center") # 因?yàn)閏lass是python的一個關(guān)鍵字,這里不想作為關(guān)鍵字使用,需要額外在后面加一個下劃線
for td in tds:print(td) # 直接打印td,得到的是所有的td標(biāo)簽
for td in tds:print(td.text) # 加個后綴就可以得到文本部分了 # 根上面一樣,也有兩種寫法
tds = soup.find_all('td',attrs={'class'='odd','align':'center'}) # 跟上面的效果是一樣的
- 獲取所有屬性值target=‘"_blank"的a標(biāo)簽的href的屬性
a_list = soup.find_all('a',target=‘"_blank")
for a in a_list:print(a) # 直接打印,得到的是a的整個標(biāo)簽頁
for a in a_list:print(a['href']) # 這里是只打印a標(biāo)簽的href的值
for a in a_list:print(a.get('href')) # 這里跟上面的效果一樣
- 獲取多個內(nèi)容,這里是從小說網(wǎng)頁上爬取小說的作者,名稱,鏈接,日期等信息,以列表的形式打印
trs = soup.find_all('tr',id = 'nr') # 這里是獲取了一本小說的整個標(biāo)簽
info = [] # 創(chuàng)建空列表,放小說的基本信息
# 下面就是從獲取的整個標(biāo)簽里面摘除想要的信息
for tr in trs:tds=tr.find_all('td') # 這里是進(jìn)一步取出標(biāo)簽信息link = tds[0],a['href'] # 獲取a標(biāo)簽的href屬性值name = tds[0].a.string # 獲取a的文本部分author = tds[2].text #獲取第3個標(biāo)簽的文本部分date = tds[4].text # 獲取第5個標(biāo)簽的文本部分info.append([link,name,author,date]) # 把信息存入列表print(info)
上面的代碼展示了獲取信息的多種方式,比如,string,get,text等等
CSS選擇器
使用該選擇器,需要使用soup.select方法
-
查找所有的tr標(biāo)簽
print(soup.select(‘tr’))
-
查找類名是odd的標(biāo)簽,這里的類就是class,也就是class=‘odd’
print(soup.select(‘.odd’)) 意思就是class=’odd‘,但是可以簡寫成’.'+‘odd’
-
查找所有id是center的標(biāo)簽
print(soup.select(‘#center’)) 意思就是id=‘center’ ,這里也是簡寫’#'+‘center’
-
組合查找
-
p標(biāo)簽下面包含屬性值為id=link2的標(biāo)簽
print(soup.select(‘p #link2’)) 先寫一個p,說明想要的是p標(biāo)簽,后空一格,說明要找下面的值,即id=link2
如果不加空格,說明這個屬性值是p的,就會按照既是p標(biāo)簽還有屬性值的p,就不再是p標(biāo)簽下面的某標(biāo)簽的屬性值
-
p標(biāo)簽下面的所有a標(biāo)簽
print(soup.select(‘p a’))
-
-
查找屬性值 target = "_blank"的 a 標(biāo)簽
print(soup.select(‘a(chǎn)[target=“_blank”]’))
-
獲取屬性值及文本內(nèi)容
獲取a標(biāo)簽的所有文本內(nèi)容,及href的屬性值
a = soup.select('a') for i in a:print(i.text)print(i.get('href'))
這些文本操作都差不多,都是篩選信息的