CSS3網頁佈局之文字佈局和文字超出處理
上次內容總結
requests作用:模擬瀏覽器發起請求
urllib:requests的前身
requests模組的編碼流程:
- 指定url
- 發起請求:
- get(url,params,headers)
- post(url,data,headers)
- 獲取響應資料
- 持久化儲存
引數動態化:
- 有些情況下我們是需要將請求引數進行更改。將get或者post請求對應的請求引數封裝到一個字典(鍵值對==請求引數)中,然後將改字典作用到get方法的params引數中或者作用到psot方法的data引數中
UA檢測(反爬機制):
- 什麼是UA:請求載體的身份標識。伺服器端會檢測請求的UA來鑑定其身份。
- 反反爬策略:UA偽裝。通過抓包工具捕獲某一款瀏覽器的UA值,封裝到字典中,且將該字典作用到headers引數中
動態載入的資料:通過另一個單獨的請求請求到的資料
對一個陌生的網站進行指定資料的爬取首先要確定爬取的資料在改網站中是否為動態載入的:
- 是:通過抓包工具實現全域性搜尋,定位動態載入資料對應的資料包,從資料包中提取請求的url和請求引數。
- 不是:就可以直接將瀏覽器位址列中的網址作為我們requests請求的url
ip被封
多次爬取導致ip被封,瀏覽器無法訪問網站臨時解決方法:
- 進入瀏覽器搜尋代理設定並開啟
- 進入區域網(LAN)設定
- 選擇為LAN使用代理伺服器並錄入代理伺服器地址與埠
- 一些代理IP網站:
- http://www.goubanjia.com/
- https://www.kuaidaili.com/
- https://www.xicidaili.com/
- http://httpbin.org/
- http://www.dailiyun.com/
- 注意:網站中免費的代理伺服器有可能會失效
- 此方法只能讓瀏覽器可以再次訪問網站,而爬蟲程式依然使用的本機IP,所以不能解決爬蟲訪問問題。
爬取http://125.35.6.84:81/xk/
需求分析:
- 企業相關資料是由動態載入的
- 通過抓包工具實現全域性搜尋,定位動態載入資料包為portalAction.do?method=getXkzsList
- 檢視資料包中的請求頭,是POST請求,Request URL: http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList
- 在Formm Data中找到請求引數
- 解析資料包的Response中Json資料並分析,沒有找到企業詳情頁的url,但找到了每家企業的id。(json解析網址:https://www.sojson.com/)
- 進入企業詳情頁,找到資料包,發現為GET請求,每一家企業的詳情頁url域名都是一樣的,只有請求引數id值不同
- 可以使用同一個域名結合不同企業的id值拼接成一家完整企業詳情頁url
- 再判斷企業詳情頁的資料是否是動態載入的,通過抓包工具檢測,發現企業詳情頁中資訊也是動態載入的
- 通過抓包工具實現全域性搜尋,定位動態載入資料包為portalAction.do?method=getXkzsById
- 檢視資料包中的請求頭,是POST請求,Request URL: http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById
- 解析資料包的Response中Json串並分析,即為我們想要的企業詳情資訊
- 在Formm Data中找到請求引數,發現請求引數為企業ID
importrequests
headers={
"User-Agent":"Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/83.0.4103.61Safari/537.36"
}
#獲取每一家企業的ID
url="http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList"
forpageinrange(1,6):#已過有354頁,現只取前5頁,一頁15條資料
data={
"on":"true",
"page":str(page),
"pageSize":"15",
"productName":"",
"conditionType":"1",
"applyname":"",
"applysn":"",
}
company_id=requests.post(url,headers=headers,data=data).json()
fordicincompany_id['list']:
_id=dic['ID']
detail_url="http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById"
data={
"id":_id
}
detail_data=requests.post(url=detail_url,data=data,headers=headers).json()
print("企業名稱:"+detail_data['epsName'])
資料解析
資料解析的作用:可以幫助我們實現聚焦爬蟲
資料解析的實現方式:
- 正則
- bs4
- xpath
- pyquery
資料解析的通用原理:聚焦爬蟲爬取的資料都被儲存在了相關的標籤之中和相關標籤的屬性中。
- 定位標籤
- 取文字或者取屬性
正則解析
#如何爬取圖片
url='https://pic.qiushibaike.com/system/pictures/12338/123385529/medium/GRIJ2WLLDDCW69P4.jpg'
img_data=requests.get(url,headers=headers).content#返回byte型別資料
withopen('./img.jpg','wb')asfp:
fp.write(img_data)
#或基於urllib,弊端:不能使用UA偽裝
fromurllibimportrequest
url='https://pic.qiushibaike.com/system/pictures/12338/123385529/medium/GRIJ2WLLDDCW69P4.jpg'
request.urlretrieveieve(url,filename='./img.jpg')
糗圖爬取1-3頁所有的圖片
urlhttps://www.qiushibaike.com/imgrank/
分析:
在瀏覽器頁面跳轉頁數,檢視每頁位址列對應的規律:第二頁為
https://www.qiushibaike.com/imgrank/page/2/
第三頁為https://www.qiushibaike.com/imgrank/page/3/
分析頁面,找到頁面對應的資料包,所搜頁面內容,可以搜到。頁面圖片不是動態載入的
分析頁面的html結構,找到想要圖片img標籤的結構規律
每個圖片的img標籤都在div標籤中,div標籤的類名為thumb
編寫正則表示式,獲取img表籤裡的src屬性
importre
importos
dirName='./imgLibs'
ifnotos.path.exists(dirName):
os.mkdir(dirName)
#設定一個通用的url模板
url='https://www.qiushibaike.com/imgrank/page/%d/'
#使用通用爬蟲將前3頁對應的頁面原始碼資料進行爬取
forpageinrange(1,4):
new_url=format(url%page)
#每一個頁碼對應的頁面原始碼資料
page_text=requests.get(new_url,headers=headers).text
#在通用爬蟲的基礎上實現聚焦爬蟲(每一個頁碼對應頁面原始碼資料中解析出圖片地址)
ex='<divclass="thumb">.*?<imgsrc="(.*?)"alt.*?</div>'
#re.S讓.匹配任意內容,包括換行符
img_src_list=re.findall(ex,page_text,re.S)
forsrcinimg_src_list:
src='https:'+src
img_name=src.split('/')[-1]
img_path=dirName+'/'+img_name#./imgLibs/xxxx.jpg
#糗事百科沒有設定UA檢測,所以可以使用urllib爬取
request.urlretrieve(src,filename=img_path)
bs4解析
bs4解析的原理:
- 例項化一個BeautifulSoup的物件,需要將即將被解析的頁面原始碼資料載入到該物件中
- 呼叫BeautifulSoup物件中的相關方法和屬性進行標籤定位和資料提取
環境的安裝:
- pip install bs4
- pip install lxml
BeautifulSoup的例項化:
BeautifulSoup(fp,'lxml')
:fp(檔案控制代碼),將本地儲存的一個html文件中的資料載入到例項化好的BeautifulSoup物件中BeautifulSoup(page_text,'lxml')
:將從網際網路上獲取的頁面原始碼資料載入到例項化好的BeautifulSoup物件中
<--新建一個test.html檔案,練習對本地檔案進行資料解析,html內容如下-->
<htmllang="en">
<head>
<metacharset="UTF-8"/>
<title>測試bs4</title>
</head>
<body>
<div>
<p>百里守約</p>
</div>
<divclass="song">
<p>李清照</p>
<p>王安石</p>
<p>蘇軾</p>
<p>柳宗元</p>
<ahref="http://www.song.com/"title="趙匡胤"target="_self">
<span>thisisspan</span>
宋朝是最強大的王朝,不是軍隊的強大,而是經濟很強大,國民都很有錢</a>
<ahref=""class="du">總為浮雲能蔽日,長安不見使人愁</a>
<imgsrc="http://www.baidu.com/meinv.jpg"alt=""/>
</div>
<divclass="tang">
<ul>
<li><ahref="http://www.baidu.com"title="qing">清明時節雨紛紛,路上行人慾斷魂,借問酒家何處有,牧童遙指杏花村</a></li>
<li><ahref="http://www.163.com"title="qin">秦時明月漢時關,萬里長征人未還,但使龍城飛將在,不教胡馬度陰山</a></li>
<li><ahref="http://www.126.com"alt="qi">岐王宅裡尋常見,崔九堂前幾度聞,正是江南好風景,落花時節又逢君</a></li>
<li><ahref="http://www.sina.com"class="du">杜甫</a></li>
<li><ahref="http://www.dudu.com"class="du">杜牧</a></li>
<li><b>杜小月</b></li>
<li><i>度蜜月</i></li>
<li><ahref="http://www.haha.com"id="feng">鳳凰臺上鳳凰遊,鳳去臺空江自流,吳宮花草埋幽徑,晉代衣冠成古丘</a></li>
</ul>
</div>
</body>
</html>
標籤的操作
定位標籤:soup.tagName
,定位到第一個出現的tagName標籤
屬性定位:
soup.find('tagName',attrName='value')
soup.find_all('tagName',attrName='value')
,返回值為列表
選擇器定位:
soup.select('選擇器')
,返回值是列表層級選擇器:>表示一個層級 空格表示多個層級
取文字,返回的是字串:
.string
:獲取直系的文字內容.text
:獲取所有的文字內容
取屬性:tagName['attrName']
frombs4importBeautifulSoup
fp=open('./test.html','r',encoding='utf-8')
soup=BeautifulSoup(fp,'lxml')
#soup返回的是html原始碼
soup.div#返回原始碼中第一次出現的div標籤
#標籤類引數:class_
soup.find('div',class_='song')#返回原始碼中第一次匹配的div標籤
soup.find('a',id="feng")#返回原始碼中第一次匹配的a標籤
soup.find_all('div',class_="song")#返回原始碼中所有匹配的div標籤
soup.select('#feng')
soup.select('.tang>ul>li')
soup.select('.tangli')#與上面的語句相同
a_tag=soup.select('#feng')[0]
a_tag.text#獲取的所有文字內容
div=soup.div
div.string#獲取直系的文字內容
div=soup.find('div',class_="song")
div.string
a_tag=soup.select('#feng')[0]
a_tag['href']#獲取屬性
爬取三國整篇內容
要求:
獲取章節名稱+章節內容
url = http://www.shicimingju.com/book/sanguoyanyi.html
fp=open('三國.txt','w',encoding='utf-8')
main_url='http://www.shicimingju.com/book/sanguoyanyi.html'
page_text=requests.get(main_url,headers=headers).text
#解析出章節名稱和章節詳情頁的url
soup=BeautifulSoup(page_text,'lxml')
#返回的列表中儲存的是一個個a標籤
a_list=soup.select('.book-mulu>ul>li>a')
foraina_list:
title=a.string
detail_url='http://www.shicimingju.com'+a['href']
detail_page_text=requests.get(detail_url,headers=headers).text
#解析詳情頁中的章節內容
soup=BeautifulSoup(detail_page_text,'lxml')
content=soup.find('div',class_='chapter_content').text
fp.write(title+':'+content+'\n')
print(title,'下載成功!')
fp.close()
xpath解析
xpath解析的實現原理
- 例項化一個etree的物件,然後將即將被解析的頁面原始碼載入到改物件中
- 使用etree物件中的xpath方法結合著不同形式的xpath表示式實現標籤定位和資料提取
環境安裝:pip install lxml
etree物件的例項化:
etree.parse('test.html')
將本地的html文件載入到etree物件中etree.HTML(page_text)
將從網際網路上獲取的頁面原始碼資料載入到etree物件中。
html文件中的html標籤是以樹狀結構存在的,根標籤是<html></html>
xpath表示式:xpath方法的返回值一定是一個列表
全域性解析:
最左側的
/
表示:xpath表示式一定要從根標籤逐層進行標籤查詢和定位最左側的
//
表示:xpath表示式可以從任意位置定位標籤非最左側的
/
:表示一個層級非最左側的
//
:表示跨多個層級屬性定位:
//tagName[@attrName="value"]
索引定位:
//tagName[index]
索引是從1開始
區域性解析:./
,對全域性解析獲得的物件再次解析,開頭必須加./
取文字:
/text()
:直系文字內容//text()
:所有的文字內容
取屬性:/@attrName
fromlxmlimportetree
tree=etree.parse('./test.html')
#返回<lxml.etree._ElementTreeat0x1a2eacafd88>
tree
#找到html標籤下的head標籤的子標籤title,該返回的列表中元素是一個物件[<Elementtitleat0x1a2ead95f48>]
tree.xpath('/html/head/title')
#從任意位置定位標籤
tree.xpath('//title')
tree.xpath('//p')
#找到所有div標籤下的所有p標籤,該返回的列表中元素有五個
tree.xpath('/html/body/div/p')
#//跨層級,找到body標籤下的所有p標籤,該返回的列表中元素有五個
tree.xpath('/html/body//p')
#屬性定位,找到類為song的div標籤
tree.xpath('//div[@class="song"]')
#索引定位,找到第七個li標籤
tree.xpath('//li[7]')
#獲取列表中的第一個文字元素
tree.xpath('//a[@id="feng"]/text()')[0]
#取所有的文字內容,返回列表
tree.xpath('//div[@class="song"]//text()')
#取屬性內容
tree.xpath('//a[@id="feng"]/@href')
爬取糗百中的段子內容和作者名稱
urlhttps://www.qiushibaike.com/text/
url='https://www.qiushibaike.com/text/'
page_text=requests.get(url,headers=headers).text
#解析內容
tree=etree.HTML(page_text)
div_list=tree.xpath('//div[@class="col1old-style-col1"]/div')
fordivindiv_list:
#實現區域性解析,開頭需加.
author=div.xpath('./div[1]/a[2]/h2/text()')[0]
content=div.xpath('./a[1]/div/span//text()')
content=''.join(content)
print(author,content)
中文亂碼的處理
爬取網站urlhttp://pic.netbian.com/4kmeinv/
處理亂碼:
處理整個爬取頁面的編碼比較耗費資源,所以應取到想要的資料,再處理其亂碼。
- 對其進行編碼
- 對編碼結果再進行解碼,解碼的編碼應用本計算機開啟不會出現亂碼的編碼,如utf-8。
注意:一般使用iso-8891-1
進行編碼,當然也可以使用其他編碼。
處理原理python基礎中涉及的編碼一章已進行了詳細講解,這裡不再進行過多解釋。
dirName='./meinvLibs'
ifnotos.path.exists(dirName):
os.mkdir(dirName)
#通用模板處理
url='http://pic.netbian.com/4kmeinv/index_%d.html'
forpageinrange(1,5):
ifpage==1:
new_url='http://pic.netbian.com/4kmeinv/'
else:
new_url=format(url%page)
page_text=requests.get(new_url,headers=headers).text
tree=etree.HTML(page_text)
a_list=tree.xpath('//div[@class="slist"]/ul/li/a')
foraina_list:
img_src='http://pic.netbian.com'+a.xpath('./img/@src')[0]
img_name=a.xpath('./b/text()')[0]
#img_name列印結果出現亂碼
#
img_name=img_name.encode('iso-8859-1').decode('gbk')
img_data=requests.get(img_src,headers=headers).content
imgPath=dirName+'/'+img_name+'.jpg'
withopen(imgPath,'wb')asfp:
fp.write(img_data)
print(img_name,'下載成功')
提高xpath的通用性
urlhttps://www.aqistudy.cn/historydata/
要求:爬取當前頁面的所有城市名稱
提高xpath的通用性:.xpath("A|B|...")
- 若A有結果,B無結果,用A;
- 若A有結果,B無結果,用A
- 若A,B都有結果,A,B都有,且返回結果為一個列表。
- 即將所有表示式能匹配到的結果返回進同一個列表中
- 很多情況下一張頁面中的標籤佈局是不一樣的,可用|解決。
page_text=requests.get('https://www.aqistudy.cn/historydata/',headers=headers).text
tree=etree.HTML(page_text)
#hot_cities=tree.xpath('//div[@class="bottom"]/ul/li/a/text()')
#all_cities=tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text()')
#提高xpath的通用性\
cities=tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text()|//div[@class="bottom"]/ul/li/a/text()')
cities
xpath表示式小技巧
- 在瀏覽器中右鍵檢查,定位到想要標籤
- 右鍵-->Copy-->Copy Xpath
即可獲取該標籤的Xpath表示式
綜合案例
urlhttp://sc.chinaz.com/jianli/free.html
要求:對免費的簡歷模板進行爬取和儲存