Python爬蟲

概念

爬蟲避免進(jìn)局子的風(fēng)險(xiǎn)

  1. 時(shí)常的優(yōu)化自己的程序,避免干擾被訪問網(wǎng)站的正常運(yùn)行
  2. 在使用,傳播獲取到的數(shù)據(jù)時(shí),審查抓取到的內(nèi)容,如果涉及到用戶隱私,商業(yè)機(jī)密等敏感內(nèi)容需要及時(shí)停止或傳播

爬蟲的分類:

  1. 通用爬蟲:抓取系統(tǒng)重要組成部分,抓取的是一整張頁面數(shù)據(jù)
  2. 聚焦爬蟲:是建立在通用爬蟲基礎(chǔ)上的,爬取的是需求頁面中特定的局部?jī)?nèi)容
  3. 增量式爬蟲:檢測(cè)網(wǎng)站中的數(shù)據(jù)更新情況,只會(huì)爬取網(wǎng)站中最新更新出來的數(shù)據(jù);

爬蟲的矛盾
反爬機(jī)制

  • 門戶網(wǎng)站,可以通過相應(yīng)的策略或者技術(shù)手段,防止爬蟲程序進(jìn)行網(wǎng)頁數(shù)據(jù)的爬取
    反反爬策略
  • 爬蟲程序可以通過制定相關(guān)的策略或技術(shù)手段,破解門戶網(wǎng)站中具備的反爬機(jī)制,從而可以獲取門戶網(wǎng)站的數(shù)據(jù);
    robots.txt協(xié)議:
  • 君子協(xié)議.規(guī)定了網(wǎng)站中哪些數(shù)據(jù)可以被爬蟲程序抓取哪些不可以;

http協(xié)議

  • 概念: 就是服務(wù)器和客戶端的進(jìn)行數(shù)據(jù)交互的一種形式,超文本傳輸協(xié)議

常用的請(qǐng)求頭:

  • User-Agent:請(qǐng)求載體的身份標(biāo)識(shí)符
  • Connection:請(qǐng)求完畢后,是斷開連接還是保持連接

常見的響應(yīng)頭

  • Content-Type:服務(wù)器響應(yīng)回復(fù)客戶端的數(shù)據(jù)類型

https協(xié)議

  • 安全的超文本傳輸協(xié)議

加密方式

  • 對(duì)稱秘鑰加密
  • 非對(duì)稱秘鑰加密
  • 證書加密

requests模塊

  • urllib 模塊
  • requests 模塊

requests模塊:python中原生的一款基于網(wǎng)絡(luò)請(qǐng)求的模塊,功能非常強(qiáng)大,簡(jiǎn)單便捷,效率極高.
作用:模擬瀏覽器發(fā)送請(qǐng)求
如何使用:

  • 指定url
  • 發(fā)起請(qǐng)求
  • 獲取響應(yīng)數(shù)據(jù)
  • 持久化存儲(chǔ)

環(huán)境安裝: pip install requests
實(shí)戰(zhàn)編碼:

import requests

heads = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.124 Safari/537.36 Edg/102.0.1245.44"}


def get_url():
    kw = input('請(qǐng)輸入檢索關(guān)鍵字')
    return "https://cn.bing.com/search?q=" + kw


def get_data(url):
    res = requests.get(url, headers=heads)
    save_file("檢索結(jié)果數(shù)據(jù)", res.text)


def save_file(name, data):
    with open(name + ".html", "w", encoding="utf-8") as wf:
        wf.write(data)


if __name__ == "__main__":
    url = get_url()
    get_data(url)

數(shù)據(jù)解析分類

  1. 正則
  2. bs4
  3. xpath (重點(diǎn))

正則解析

ex=‘<div class="thumb">.*?<img src="(.*?)" alt.*?</div>’

數(shù)據(jù)解析概況:
解析的局部的文本內(nèi)容都會(huì)在標(biāo)簽之間或者標(biāo)簽對(duì)應(yīng)的屬性中進(jìn)行存儲(chǔ)

  1. 進(jìn)行指定標(biāo)簽定位
  2. 標(biāo)簽或者標(biāo)簽對(duì)應(yīng)的屬性中存儲(chǔ)的數(shù)據(jù)進(jìn)行提?。ń馕觯?/li>

bs4進(jìn)行數(shù)據(jù)解析

  1. 數(shù)據(jù)解析的原理
  2. 標(biāo)簽定位
  3. 提取標(biāo)簽、標(biāo)簽屬性中存儲(chǔ)的數(shù)據(jù)值
  4. bs4數(shù)據(jù)解析原理
    1. 通過實(shí)例化一個(gè)BeautifulSoup對(duì)象,并且將頁面數(shù)據(jù)加載到該對(duì)象中
    2. 通過調(diào)用BeautifulSoup對(duì)象中的屬性或方法進(jìn)行標(biāo)簽定位和數(shù)據(jù)提取

環(huán)境安裝

  1. pip install bs4
  2. pip install lxml

如何實(shí)例化BeautifulSoup對(duì)象:
from bs4 import BeautifulSoup
對(duì)象的實(shí)例化

  1. 將本地的html文檔中的數(shù)據(jù)加載到該對(duì)象中
    fp=open('./test.html','r',encoding='utf-8')
    soup=BeautifulSoup(fp,'lxml')
  2. 將互聯(lián)網(wǎng)上獲取的頁面源碼加載到該對(duì)象中
    page_text = response.text
    soup=BeautifulSoup(page_text,'lxml')

提供的用于數(shù)據(jù)解析的方法和屬性
soup.tagName:返回的是文檔中第一次出現(xiàn)的tagName對(duì)應(yīng)的標(biāo)簽,如soup.title
soup.find():find('tagname')等同于soup.div-屬性定位soup.find('div',class_/id/attr='song)
soup.find_all('tagName',)返回符合要求的所有標(biāo)簽(列表)
select:
select('某種選擇器(id,class,標(biāo)簽...選擇器'))返回的是一個(gè)列表.
層級(jí)選擇器:
--soup.select('.tang > ul > li > a')>標(biāo)識(shí)是一個(gè)層級(jí)
--soup.select('.tang > ul a')>空格標(biāo)識(shí)多個(gè)層級(jí)
獲取標(biāo)簽之間的數(shù)據(jù)
soup.a.text/string.get_text()text/get_text()可以過去摸一個(gè)標(biāo)簽中所有的文本內(nèi)容
string 值可以獲取該標(biāo)簽下的文本內(nèi)容
獲取標(biāo)簽中的屬性值
soup.a['href']

Xpath解析

xpath解析:最常用最便捷的高效的一種解析方式,通用性.
xpath 解析原理:

  1. 實(shí)例化一個(gè)etree的對(duì)象,且需要將其解析源碼數(shù)據(jù)加載到該對(duì)象中
  2. 調(diào)用etree對(duì)象中的xpath方法結(jié)合著xpath表達(dá)式實(shí)現(xiàn)標(biāo)簽的定位和內(nèi)容的捕獲.

環(huán)境的安裝:

  1. pip install lxml
    使用:from lxml import etree
    如何實(shí)例化一個(gè)etree對(duì)象,且需要將解析的頁面源碼數(shù)據(jù)加載到該對(duì)象中.
  2. 將本地的html文檔的數(shù)據(jù)源碼加載到etree對(duì)象中:etree.parse(filePath, etree.HTMLParser())
  3. 可以將互聯(lián)網(wǎng)上獲取的源碼數(shù)據(jù)加載到etree對(duì)象中:etree.HTML(str)

使用:result = html.xpath('//li')
print(result)
xpath表達(dá)式:

  1. /表示的是從根節(jié)點(diǎn)開始,表示的是一個(gè)層級(jí)
  2. //表示的是多個(gè)層級(jí),可以表示從任意位置開始
  3. 屬性定位://div[@class="song"] tag[@attrName="attrValue"]
  4. 索引定位://div[@class="song"]/p[3] 索引是從1開始的
  5. 取文本:
    1. /text()獲取標(biāo)簽中直系的文本內(nèi)容如://p[1]/span/a/text()
    2. //text()獲取標(biāo)簽中非直系的文本內(nèi)容(該標(biāo)簽所有文本內(nèi)容)
  6. 取屬性:
    1. /@attrName //img[@id="imgCode"]/@src'

驗(yàn)證碼識(shí)別

驗(yàn)證碼和爬蟲之間的愛恨情仇?
反爬蟲機(jī)制:驗(yàn)證碼。識(shí)別驗(yàn)證碼圖片中的數(shù)據(jù),用于模擬登錄操作
識(shí)別驗(yàn)證碼的操作:

  1. 人工肉眼識(shí)別
  2. 第三方 自動(dòng)識(shí)別驗(yàn)證碼識(shí)別技術(shù)開發(fā)文檔-云碼 (jfbym.com)
    ddddocr的庫使用

云打碼使用

  1. 進(jìn)入打碼平臺(tái)注冊(cè)賬號(hào)登陸后使用接口調(diào)用即可
import requests
import base64

def get_ocr_img(filePath):
    url='https://www.jfbym.com/api/YmServer/verifyapi'
    base=encode_base64(filePath)
    data={
        "image":base,
        "token":"lvfJhEHuS+2Kk5HWWFGz2pYZOOiaDvLeIauN35puIc=",
        "type":"10101"
    }
    headers={
        "Content-Type":"application/x-www-form-urlencoded"
    }
    res=requests.post(url,data=data,headers=headers)
    print(res.text)

def encode_base64(file):
    with open(file, 'rb') as f:
        img_data = f.read()
        base64_data = base64.b64encode(img_data)
        print(type(base64_data))
        # print(base64_data)
        # 如果想要在瀏覽器上訪問base64格式圖片,需要在前面加上:data:image/jpeg;base64,
        base64_str = str(base64_data, 'utf-8')
        print(base64_str)
        return base64_data


def decode_base64(base64_data):
    with open('./images/base64.jpg', 'wb') as file:
        img = base64.b64decode(base64_data)
        file.write(img)


if __name__ == '__main__':
        get_ocr_img('./yzm.jpg')

http/https協(xié)議特征:無狀態(tài)。
沒有請(qǐng)求到對(duì)應(yīng)頁面數(shù)據(jù)的原因

  • 發(fā)起的第二次基于個(gè)人主頁面請(qǐng)求的時(shí)候。服務(wù)器端并不知道該此請(qǐng)求是基于登錄狀態(tài)下的請(qǐng)求。

cookie:用來讓服務(wù)器端記錄客戶端的相應(yīng)狀態(tài):

  • 手動(dòng)處理:通過抓包工具獲取cookie值。將該值封裝到headers中(不建議)
  • 自動(dòng)處理:
    1. 創(chuàng)建一個(gè)session對(duì)象:session=requests.Session()
    2. 使用session對(duì)象進(jìn)行模擬登錄post請(qǐng)求的發(fā)送(cookie就會(huì)被存儲(chǔ)在session中)
    3. session對(duì)象對(duì)個(gè)人主頁對(duì)應(yīng)的get請(qǐng)求發(fā)送(攜帶了cookie)

代理:破解封IP這種反爬機(jī)制。

什么是代理?-代理服務(wù)器
代理的作用:突破自身IP訪問的限制,隱藏自身的真實(shí)IP
代理相關(guān)網(wǎng)站:快代理,西祠代理,www.goubanjia.com
代理IP的類型

  • http:應(yīng)用到http協(xié)議對(duì)應(yīng)的url中
  • https:應(yīng)用到https協(xié)議對(duì)應(yīng)的url中

使用方法:res=requests.get(url,headers=headers,proxies={'https':'103.103.3.6:8080'}).text
代理ip的匿名度

  • 透明:服務(wù)器知道該次請(qǐng)求使用了代理,也知道對(duì)應(yīng)真是的IP地址
  • 匿名:知道了使用代理,不是到真實(shí)IP
  • 高匿:不知道使用了代理,不知道真是IP
import requests
url ='https://ip.hao86.com/'
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36 Edg/103.0.1264.37'}
res=requests.get(url,headers=headers,proxies={'https':'10.103.3.6:8080'}).text
with open('./dd.html','w',encoding='utf-8') as wf:
    wf.write(res)

高性能異步爬蟲

目的:在爬蟲中使用異步實(shí)現(xiàn)高性能的數(shù)據(jù)爬取操作
異步爬蟲的方式:

  • 多線程,多進(jìn)程:(不建議)
    好處:可以為相關(guān)阻塞的操作單獨(dú)開啟線程或進(jìn)程,阻塞操作就可以異步執(zhí)行.
    弊端:無法限制的開啟多線程或多進(jìn)程.
  • 線程池,進(jìn)程池(適當(dāng)使用)
    好處:我們可以降低系統(tǒng)對(duì)進(jìn)程或線程創(chuàng)建和銷毀的一個(gè)頻率,從而很好的降低系統(tǒng)的開銷
    弊端:池中線程或進(jìn)程的數(shù)量是有上限.
  • 單線程+異步協(xié)程(推薦):
    event_loop:事件循環(huán),相當(dāng)于一個(gè)無限循環(huán),我們可以把一些函數(shù)注冊(cè)到這個(gè)事件循環(huán)上,當(dāng)滿足某些條件的時(shí)候,函數(shù)就會(huì)被循環(huán)執(zhí)行
    coroutine 協(xié)程對(duì)象,我們可以將協(xié)程對(duì)象注冊(cè)到事件循環(huán)中,它會(huì)被事件循環(huán)調(diào)用.我們可以使用async關(guān)鍵字來頂一個(gè)方法,這個(gè)方法在調(diào)用時(shí)不會(huì)被執(zhí)行,而是返回一個(gè)協(xié)程對(duì)象.
    task 任務(wù),他是對(duì)協(xié)程對(duì)象的進(jìn)一步封裝,包含了任務(wù)的各個(gè)狀態(tài)
    future:代表將來執(zhí)行或者還沒有執(zhí)行的任務(wù),實(shí)際上和task沒有本質(zhì)區(qū)別
    async定義一個(gè)協(xié)程
    await 用來掛起阻塞方法的執(zhí)行.

不使用線程池方式

import time
def get_page(str):
    print('start-',str)
    time.sleep(2)
    print('end-',str)

name_list=['xiaozi','aaa','bbb','ccc']

start_time=time.time()
for i  in range(len(name_list)):
    get_page(name_list[i])

end_time=time.time()
print('%d sdde'%(end_time-start_time)) # 8 sdde

使用線程池方式

import time
from multiprocessing.dummy import Pool
def get_page(str):
    print('start-',str)
    time.sleep(2)
    print('end-',str)

name_list=['xiaozi','aaa','bbb','ccc']

start_time=time.time()
# for i  in range(len(name_list)):
#     get_page(name_list[i])
# 實(shí)例化線程池對(duì)象
pool=Pool(4)# 開啟4個(gè)線程池
# 將列表中每一個(gè)列表元素傳遞給get_page進(jìn)行處理,返回的結(jié)果就是get_page的return
pool.map(get_page,name_list)
end_time=time.time()
print('%d sdde'%(end_time-start_time)) # 2 sdde

使用單線程+異步協(xié)程

import asyncio
async def request_url(url):
    print('正在請(qǐng)求的url時(shí)',url)
    print('請(qǐng)求成功',url)
    return url

c=request_url('www.baidu.com')
# 創(chuàng)建一個(gè)事件循環(huán)對(duì)象
# loop=asyncio.get_event_loop()
# # 將協(xié)程對(duì)象注冊(cè)到loop中然后啟動(dòng)loop
# loop.run_until_complete(c)
# asyncio.run(c) # 上述的封裝方法

# task的使用
# loop=asyncio.get_event_loop()
# task=loop.create_task(c,name='task1')
# print(task)
# loop.run_until_complete(task)
# # future
# loop=asyncio.get_event_loop()
# task=asyncio.ensure_future(c)
# print(task)
# loop.run_until_complete(task)
# 綁定回調(diào)
def callback_func(task):
    print(task.result())
loop=asyncio.get_event_loop()
task=asyncio.ensure_future(c)
# 將回調(diào)函數(shù)綁定到任務(wù)對(duì)象中,task執(zhí)行完成后就會(huì)調(diào)用該函數(shù)
task.add_done_callback(callback_func)
loop.run_until_complete(task)

import asyncio
import time
async def request_url(url):
    print('正在下載',url)
    await asyncio.sleep(2)
    print('下載完成',url)

start=time.time()
urls=[
    'baidu',
'google',
'sougou',
'bing'
]
# stasks=[]
# for ul in urls:
#     c=request_url(ul)
#     task=asyncio.ensure_future(c)
#     stasks.append(task)

tasks=[request_url(url) for url in urls]

loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
ends=time.time()
print(ends-start)

案例

import asyncio
import time
import requests
import aiohttp
urls=[
    'http://127.0.0.1:5000/b1',
'http://127.0.0.1:5000/b2',
'http://127.0.0.1:5000/b3',
]
async def get_page(url):
    print('正在下載',url)
    # requests.get是基于同步,必須使用基于異步的網(wǎng)絡(luò)請(qǐng)求模塊進(jìn)行指定url的請(qǐng)求發(fā)送,或者給他弄成線程再轉(zhuǎn)換為協(xié)程
    # aiohttp基于異步網(wǎng)絡(luò)請(qǐng)求的模塊
    async with aiohttp.ClientSession() as session:
        # get(),post()
        # headers,params/data,proxy='http://ip:port'
        async  with session.get(url) as res:
           pageText= await res.text()
    print('下載完畢',pageText)

tasks=[get_page(url) for url in urls]

start=time.time()
asyncio.run(asyncio.wait(tasks))
ends=time.time()
print('總耗時(shí)',ends-start) # 總耗時(shí) 2.0147018432617188

錯(cuò)誤演示//request沒有協(xié)程

import asyncio
import time
import requests

urls=[
    'http://127.0.0.1:5000/b1',
'http://127.0.0.1:5000/b2',
'http://127.0.0.1:5000/b3',
]
async def get_page(url):
    print('正在下載',url)
    res=requests.get(url)
    print('下載完畢',res.text)

tasks=[get_page(url) for url in urls]

start=time.time()
asyncio.run(asyncio.wait(tasks))
ends=time.time()
print('總耗時(shí)',ends-start)

后端代碼 類似下方方式

from flask import Flask
import time
app=Flask(__name__)
@app.route('/b1')
def index_b1():
    time.sleep(2)
    return 'Hello b1'

@app.route('/b2')
def index_b2():
    time.sleep(2)
    return 'Hello b2'

@app.route('/b3')
def index_b3():
    time.sleep(2)
    return 'Hello b3'

if __name__=='__main__':
    app.run(threaded=True)



擴(kuò)展Python自帶的網(wǎng)絡(luò)請(qǐng)求庫

示例
發(fā)送get請(qǐng)求

import urllib.request

url='https://funletu.com/dong-tai/page/2'
req=urllib.request.Request(url)
with urllib.request.urlopen(req) as response:
    data=response.read()
    ss=data.decode()
    print(ss)

發(fā)送post請(qǐng)求

import urllib.request
import urllib.parse
url='https://funletu.com/dong-tai/page/2'
params_dict={'id':123,'query':'all'}
params_str=urllib.parse.urlencode(params_dict)
params_bytes=params_str.encode()
req=urllib.request.Request(url,data=params_bytes)
with urllib.request.urlopen(req) as response:
    data=response.read()
    ss=data.decode()
    print(ss)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容