爬蟲2 資料解析 --圖片
### 回顧
- 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
### 今日內容
- 資料解析
- 資料解析的作用:
- 可以幫助我們實現聚焦爬蟲
- 資料解析的實現方式:
- 正則
- bs4
- xpath
- pyquery
- 資料解析的通用原理
- 問題1:聚焦爬蟲爬取的資料是儲存在哪裡的?
- 都被儲存在了相關的標籤之中and相關標籤的屬性中
- 1.定位標籤
- 2.取文字或者取屬性
如何爬取圖片呢?
import requests headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36爬取圖片' } #如何爬取圖片 url = 'https://pic.qiushibaike.com/system/pictures/12223/122231866/medium/IZ3H2HQN8W52V135.jpg' img_data = requests.get(url,headers=headers).content #byte型別資料 with open('./img.jpg','wb') as fp: fp.write(img_data)
2、引用 urllib(建議不用,因為不能UA偽裝)
#弊端:不能使用UA偽裝 from urllib import request url = 'https://pic.qiushibaike.com/system/pictures/12223/122231866/medium/IZ3H2HQN8W52V135.jpg' request.urlretrieve(url,filename='./qiutu.jpg')urllib
到糗事百科 爬取圖片
import re import os #1.使用通用爬蟲將前3頁對應的頁面原始碼資料進行爬取 #通用的url模板(不可變) dirName = './imgLibs' if not os.path.exists(dirName): os.mkdir(dirName) url = 'https://www.qiushibaike.com/pic/page/%d/' for page in range(1,4): new_url = format(url%page) page_text = requests.get(new_url,headers=headers).text #每一個頁碼對應的頁面原始碼資料 #在通用爬蟲的基礎上實現聚焦爬蟲(每一個頁碼對應頁面原始碼資料中解析出圖片地址) ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>' img_src_list = re.findall(ex,page_text,re.S) for src in img_src_list: src = 'https:'+src img_name = src.split('/')[-1] img_path = dirName+'/'+img_name #./imgLibs/xxxx.jpg request.urlretrieve(src,filename=img_path) print(img_name,'下載成功!!!')#糗圖爬取1-3頁所有的圖片
- bs4解析
- bs4解析的原理:
- 例項化一個BeautifulSoup的物件,需要將即將被解析的頁面原始碼資料載入到該物件中
- 呼叫BeautifulSoup物件中的相關方法和屬性進行標籤定位和資料提取
- 環境的安裝:
- pip install bs4
- pip install lxml
- BeautifulSoup的例項化:
- BeautifulSoup(fp,'lxml'):將本地儲存的一個html文件中的資料載入到例項化好的BeautifulSoup物件中
- BeautifulSoup(page_text,'lxml'):將從互聯網上獲取的頁面原始碼資料載入到例項化好的BeautifulSoup物件中
- 定位標籤的操作:
- soup.tagName:定位到第一個出現的tagName標籤 soup.div
- 屬性定位:soup.find('tagName',attrName='value') soup.find('div',class_='c1')
- 屬性定位:soup.find_all('tagName',attrName='value'),返回值為列表: soup.find_all('div',id='d1')
- 選擇器定位:soup.select('選擇器') soup.select('#feng')
- 層級選擇器:>表示一個層級 空格表示多個層級 soup.select('.tang > ul > li')
- 取文字
- .string:獲取直系的文字內容
- .text:獲取所有的文字內容
- 取屬性
- tagName['attrName']
from bs4 import BeautifulSoup fp = open('./test.html','r',encoding='utf-8') soup = BeautifulSoup(fp,'lxml') # 本地檔案控制代碼 soup.div soup.find('div',class_='song') soup.find('a',id="feng") soup.find_all('div',class_="song") soup.select('#feng') soup.select('.tang > ul > li') # >表示一層級(直系) soup.select('.tang li') # 空格表示多個層級(孫子輩) 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']
Ok 來下載小說吧
fp = open('sanguo.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_list = soup.select('.book-mulu > ul > li > a') #返回的列表中儲存的是一個個a標籤 for a in a_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方法的返回值一定是一個列表
- 最左側的/表示:xpath表示式一定要從根標籤逐層進行標籤查詢和定位
- 最左側的//表示:xpath表示式可以從任意位置定位標籤
- 非最左側的/:表示一個層級
- 非最左側的//:表示誇多個層級
- 屬性定位://tagName[@attrName="value"] //div[@class='c1']
- 索引定位://tagName[index] 索引是從1開始 //li[1]
- 取文字:
- /text():直系文字內容
- //text():所有的文字內容
- 取屬性:
- /@attrName @ href
from lxml import etree tree = etree.parse('./test.html') tree.xpath('/html/head/title') tree.xpath('//title') tree.xpath('/html/body//p') tree.xpath('//p') tree.xpath('//div[@class="song"]') tree.xpath('//li[7]') tree.xpath('//a[@id="feng"]/text()')[0] tree.xpath('//div[@class="song"]//text()') tree.xpath('//a[@id="feng"]/@href')
from lxml import etree url = 'https://www.qiushibaike.com/text/' page_text = requests.get(url,headers=headers).text #解析內容 tree = etree.HTML(page_text) div_list = tree.xpath('//div[@id="content-left"]/div') for div in div_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)
#爬取糗百中的段子內容和作者名稱 - 有時候爬取的資料出現亂碼 可以參考下面:
- img_name = img_name.encode('iso-8859-1').decode('gbk')
- 先用iso-8859-1 來編碼 再用gbk 解碼 即可
- img_name = img_name.encode('iso-8859-1').decode('gbk')
-
import os dirName = './meinvLibs' if not os.path.exists(dirName): os.mkdir(dirName) url = 'http://pic.netbian.com/4kmeinv/index_%d.html' for page in range(1,11): if page == 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') for a in a_list: img_src = 'http://pic.netbian.com'+a.xpath('./img/@src')[0] img_name = a.xpath('./b/text()')[0] img_name = img_name.encode('iso-8859-1').decode('gbk') img_data = requests.get(img_src,headers=headers).content imgPath = dirName+'/'+img_name+'.jpg' with open(imgPath,'wb') as fp: fp.write(img_data) print(img_name,'下載成功!!!')
#http://pic.netbian.com/4kmeinv/中文亂碼的處理 - 有時候網頁變化莫測 ,即同一個位置的標籤有兩種或兩種以上的寫法 會對爬蟲爬取的規律性造成巨大麻煩
- 可以用下面的方法來解決一下
-
#https://www.aqistudy.cn/historydata/ 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()') cities = tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text() | //div[@class="bottom"]/ul/li/a/text()') #提高xpath的通用性 cities
所有城市名稱- 上面的程式碼是這麼寫的cities = tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text() | //div[@class="bottom"]/ul/li/a/text()')
- 增加了 | 來並列兩個表示式 ,即滿足第一個表示式則 第二個表示式忽略,第一個不滿足,即用第二個表示式!
- /@attrName @ href
### 回顧- 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