爬蟲之數據解析


  一、啥是數據解析

  在上一篇關於爬蟲的博客里,我提到過,整個爬蟲分為四個部分,上一篇博客已經完成了前兩步,也就是我說的最難的地方,接下來這一步數據解析不是很難,但就是很煩人,但只要你有耐心,一步一步查找、排除就會提取出目標信息,這一步就相當於從接收到的龐大數據中提取出真正想要、有意義的信息,所以對於爬蟲來說,應該是很重要的。

  數據解析有三種方式,一是通過正則表達式,在python中就是利用re模塊;二是xpath;三是利用BeautifulSoup。

  二、正則表達式

  之前我們在學模塊的時候講過正則表達式,在這就不細說,獻上經常用到的

   單字符:
        . : 除換行以外所有字符
        [] :[aoe] [a-w] 匹配集合中任意一個字符
        \d :數字  [0-9]
        \D : 非數字
        \w :數字、字母、下划線
        \W : 非\w
        \s :所有的空白字符包,括空格、制表符、換頁符等等。等價於 [ \f\n\r\t\v]。
        \S : 非空白
    數量修飾:
        * : 任意多次  >=0
        + : 至少1次   >=1
        ? : 可有可無  0次或者1次
        {m} :固定m次 hello{3,}
        {m,} :至少m次
        {m,n} :m-n次
    邊界:
        $ : 以某某結尾 
        ^ : 以某某開頭
    分組:
        (ab)  
    貪婪模式: .*
    非貪婪(惰性)模式: .*?

    re.I : 忽略大小寫
    re.M :多行匹配
    re.S :單行匹配

    re.sub(正則表達式, 替換內容, 字符串)

  三、xpath

  1,常用表達式

屬性定位:
    #找到class屬性值為song的div標簽
    //div[@class="song"] 
層級&索引定位:
    #找到class屬性值為tang的div的直系子標簽ul下的第二個子標簽li下的直系子標簽a
    //div[@class="tang"]/ul/li[2]/a
邏輯運算:
    #找到href屬性值為空且class屬性值為du的a標簽
    //a[@href="" and @class="du"]
模糊匹配:
    //div[contains(@class, "ng")]
    //div[starts-with(@class, "ta")]
取文本:
    # /表示獲取某個標簽下的文本內容
    # //表示獲取某個標簽下的文本內容和所有子標簽下的文本內容
    //div[@class="song"]/p[1]/text()
    //div[@class="tang"]//text()
取屬性:
    //div[@class="tang"]//li[2]/a/@href

我們在使用xpath時,想要把字符串轉化為etree對象:
tree=etree.parse(文件名)#這種是把一個本地文件轉化成rtree對象
tree=etree.HTML(html標簽字符串)
tree.xpath(xpath表達式) #這樣就可以通過找到某個標簽,取出標簽的某個屬性就得到想要的結果

  2,示例一,爬取糗事百科圖片,保存在本地

import requests
from lxml import etree
#這是請求的路徑
url='https://www.qiushibaike.com/pic/' #這是偽造的瀏覽器UA
headers
={ 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36' }
#content拿到的是頁面代碼 content
=requests.get(url=url,headers=headers).text
#把這個頁面字符串代碼轉換成etree對象 tree
=etree.HTML(content)
#這是拿到所有class=‘thumb’的div標簽下的img標簽的src屬性,返回的是一個列表 img_src_list
=tree.xpath('//div[@class="thumb"]//img/@src')
#循環每個src,然后再去訪問,拿到圖片的字節數據,存放於JPG文件,就得到每張圖片了
for img_src in img_src_list: c1=requests.get(url='https:'+img_src,headers=headers).content with open('%s.jpg'%img_src[:5],'wb') as f: f.write(c1)

  3,示例二,爬取煎蛋網的圖片

  這個就不是那么簡單了,可以說是及其的難,我們用瀏覽器去訪問一下煎蛋網,查看一下每張圖片的src。

在這個元素的頁面上,也就是加載完畢后的HTML文件,上面可以看到img的src屬性,不用猜,這個肯定是圖片的地址,很是興奮,急急忙忙的寫程序,訪問頁面,拿去img的src值,然后再發起請求拿到圖片數據,保存下來,運行程序出錯了,不是預期的結果。在這,給大家分享一個反爬機制,對於圖片的src屬性並不是直接寫在html頁面上的,而是在加載頁面時用js得到img的src屬性,然后賦值過去,其實我們可以點開network,查看response,這個response才是真正返回的HTML文件內容,也就是接收的內容。如下圖:

  從response來看,它的所有圖片的src都是一樣的,說明並不是圖片真正的輸入窗路徑,后面跟了一個span標簽,class為img-hash,文本內容為一大段字符,可以猜出這是一個hash值,這個值就是img的src加密后的hash值,所以在加載頁面時,通過js把加密的字符解開就是img的src屬性,然后再賦給src(別問我是咋知道,我看別人這樣寫的,但確實是對的),這種通過js來動態加載的頁面是一種反爬機制,而且是一種讓人很頭疼的反爬機制。

  現在我們想要拿到他的src,就需要我們從返回的html文件中取出每個img-hash值,然后解密,得到真正的src,然后再對src發起請求。

import requests
from lxml import etree
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
}
import base64
url='http://jandan.net/ooxx'
content=requests.get(url=url,headers=headers).text
tree=etree.HTML(content)
hash_list=tree.xpath('//span[@class="img-hash"]/text()')   #這是拿到了所有的img-hsah值,存放在一個列表中

for i in hash_list:
    ur=base64.b64decode(i).decode()           #這里用base64解密(不要問我為啥用這個解密,你咋知道的。大佬說,在js代碼發現有base64和md5的字樣,然而md5是不可逆的,所以就是base64了)
    con=requests.get(url='http:'+ur,headers=headers).content
    with open('%s.jpg'%i,'wb') as f:
        f.write(con)

  四、BeautifulSoup

  1,方法

from bs4 import BeautifulSoup
soup = BeautifulSoup(open('本地文件'), 'lxml')    #這是把一個本地文件轉換成BeautifulSoup對象
soup = BeautifulSoup('字符串類型或者字節類型', 'lxml')#這是把HTML字符串轉換成BeautifulSoup對象

基礎鞏固:
    (1)根據標簽名查找
        - soup.a   只能找到第一個a標簽,其他標簽一樣
    (2)獲取屬性
        - soup.a.attrs  獲取第一個a標簽所有的屬性和屬性值,返回一個字典
        - soup.a.attrs['href']   獲取href屬性
        - soup.a['href']   也可簡寫為這種形式
    (3)獲取內容
        - soup.a.string
        - soup.a.text
        - soup.a.get_text()
       【注意】如果標簽還有標簽,那么string獲取到的結果為None,而其它兩個,可以獲取文本內容
    (4)find:找到第一個符合要求的標簽
        - soup.find('a')  
        - soup.find('a', title="xxx")
        - soup.find('a', alt="xxx")
        - soup.find('a', class_="xxx")    #按類查找,得在把class寫成class_
        - soup.find('a', id="xxx")
    (5)find_all:找到所有符合要求的標簽
        - soup.find_all('a')
        - soup.find_all(['a','b']) 找到所有的a和b標簽
        - soup.find_all('a', limit=2)  限制前兩個
    (6)根據選擇器選擇指定的內容       #選擇器的規則和css一模一樣,
               select:soup.select('#feng')
        - 常見的選擇器:標簽選擇器(a)、類選擇器(.)、id選擇器(#)、層級選擇器
            - 層級選擇器:
                div .dudu #lala .meme .xixi  下面好多級
                div > p > a > .lala          只能是下面一級
        【注意】select選擇器返回永遠是列表,需要通過下標提取指定的對象

  2,實例一,爬取抽屜網的新聞標題和連接

from bs4 import BeautifulSoup
import requests
url='https://dig.chouti.com/'
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
}
con=requests.get(url=url,headers=headers).text
#這是實例化一個BeautifulSoup對象,對象就可以使用find、find_all等方法 soup
=BeautifulSoup(con,'lxml') a_list=soup.find_all('a',class_="show-content color-chag")#這是拿到了很多的a標簽, data_list=[] for a in a_list: dic={} dic['url']=a['href'] dic['content']=a.text.strip() data_list.append(dic) print(data_list)

  3,實例二,爬取58同城的房源信息

from bs4 import BeautifulSoup
import requests
import xlwt
import re
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
}
#這是創建一個Excel,並創建一個sheet表,設置屬性 workbook
= xlwt.Workbook(encoding='ascii') worksheet = workbook.add_sheet('My Worksheet') worksheet.col(0).width = 14000 worksheet.col(5).width = 24000 #發送請求 url='https://sz.58.com/ershoufang/?utm_source=market&spm=u-2d2yxv86y3v43nkddh1.BDPCPZ_BT&PGTID=0d30000c-0000-4591-0324-370565eccba8&ClickID=1' res=requests.get(url=url,headers=headers) con=res.text
#實例化一個對象 soup
=BeautifulSoup(con,'lxml') ss=soup.find('ul',class_='house-list-wrap') li_list=ss.find_all('li') #這是拿到了所有的li標簽 patter=re.compile(r'\s',re.S)
#這是循環每個li標簽,這里拿到的每個li標簽還是一個BeautifulSoup對象,一樣擁有find、find_all等方法,對每個li標簽處理拿到每個房源的各種信息,然后寫入Excel中
for num in range(len(li_list)): worksheet.write(num, 0, label=li_list[num].find('a',tongji_label="listclick").text.strip()) p1=li_list[num].find_all('p')[0] span_list=p1.find_all('span') worksheet.write(num, 1, label=patter.sub('',span_list[0].text)) worksheet.write(num, 2, label=patter.sub('',span_list[1].text)) worksheet.write(num, 3, label=patter.sub('',span_list[2].text)) worksheet.write(num, 4, label=patter.sub('',span_list[3].text)) worksheet.write(num,5, label=patter.sub('',li_list[num].find('div',class_='jjrinfo').text)) worksheet.write(num, 6, label=li_list[num].find('p',class_='sum').text) worksheet.write(num, 7, label=li_list[num].find('p',class_='unit').text) workbook.save('myWorkbook.xls')

  4,實例三,爬取github

  github是需要登錄驗證的,所以我們照我上一篇講的模擬登錄的步驟,先用瀏覽器輸入一組錯誤信息,點擊登錄,找的登錄發送的路徑和數據結構,

  明顯發現這就是登錄請求的路徑,數據結構拿到了,再去拿到請求的路徑

  這下就可以發送請求,我最先訪問的是login頁面,得到cookie,帶這個cookie和data數據,往登錄的路徑發送請求,但不得行。於是乎回來看了一看,要求的數據結構,其中有個叫token的東西,怎么那么熟悉,這個不是那個隨機值CSRF-token,我就再去看了一下HTML頁面,

  確實是基於form表單發送請求的CSRF-token,這個東西是一個隨機值,所以我的程序得想去訪問login頁面,拿到登陸頁面,取得這個token值,放在data數據里,我之前程序的其他部分就不用變了,於是乎就成功了。

import requests
import re
url='https://github.com/login'

url1='https://github.com/session'
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36',
}
session=requests.session()
res1=session.get(url=url,headers=headers)
token=re.findall(r'name="authenticity_token".*?value="(?P<name>.*?)"',res1.text,re.S)[0]      #這就是用BeautifulSoup取得token值
data={
'commit': 'Sign in',
'utf8': '',
'authenticity_token': token,
'login': 'xxxxxx',
'password':'xxxxx'
}
session.post(url=url1,data=data,headers=headers)
url2='https://github.com/'
res=session.get(url=url2,headers=headers)
with open('github1.html','wb') as f:
    f.write(res.content)

   注意:

  1,xpath和BeautifulSoup都是針對標簽的解析方式,意思就是字符串得是一個標簽字符串,其次是要先找到標簽,然后獲取標簽的某個屬性值

  2,xpath和BeautifulSoup找的標簽,依然是一個對象,意思就是同樣可以用那些方法,就不用我們把找到的標簽再次實例化成對象


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM