Python:網絡爬蟲相當利器


網絡爬蟲,又稱為網頁蜘蛛(WebSpider),非常形象的一個名字。如果你把整個互聯網想象成類似於蜘蛛網一樣的構造,那么我們這只爬蟲,就是要在上邊爬來爬去,順便獲得我們需要的資源。我們之所以能夠通過百度或谷歌這樣的搜索引擎檢索到你的網頁,靠的就是他們大量的爬蟲每天在互聯網上爬來爬去,對網頁中的每個關鍵詞進行索引,建立索引數據庫。在經過復雜的算法進行排序后,這些結果將按照與搜索關鍵詞的相關度高低,依次 

最近,主要的任務就是開發性感美女圖片大全,使用python進行圖片采集

1 urllib模塊

urllib模塊實際上是綜合了url和lib的一個包。

url的一般格式為:

protocol://hostname[:port]/path/[;parameters][?query]#fragment

URL 由三部分組成:

第一部分是協議:http,https,ftp,file,ed2k…

第二部分是存放資源的服務器的域名系統或IP地址(有時候要包含端口號,各種傳輸協議都有默認的端口,如http的默認端口是80)

第三部分是資源的具體地址,如目錄或者文件名等

舉一個例子說明:

復制代碼
import urllib.request
response = urllib.request.urlopen("http://www.meimei169.com/")
html = response.read()
print(html) #二進制數據

html = html.decode('utf-8') #對二進制數據解碼
print(html)
復制代碼

當遇到不了解的模塊時,可通過IDLE中Help中打開Python的文檔進行搜索查看,也可以使用print(模塊名.__doc__)或者help(模塊名)進行屬性和使用方法的查看。如下為文檔中urlopen的用法:

實例1:在placekitten網站下載一只貓的圖片

復制代碼
import urllib.request
response = urllib.request.urlopen("http://www.meimei169.com/") #urlopen返回一個對象
cat_img = response.read()  #對象均可用print()打印出來  # response.geturl() 得到url  #response.getcode() 返回值200,說明網站正常響應  response.info()得到文件信息
with open('cat_300_300.jpg','wb') as f:
    f.write(cat_img)
復制代碼

可看到在當前運行目錄下已成功下載了圖片。

urlopen的url參數既可以是字符串也可以是一個request對象,則我們還可以將代碼寫成如下形式:

復制代碼
import urllib.request
req = urllib.request.Request("http://www.meimei169.com/")
response = urllib.request.urlopen(req)

cat_img = response.read()
with open('cat_500_600.jpg','wb') as f:
    f.write(cat_img)
復制代碼

實例2:利用百度翻譯進行翻譯

小甲魚的視頻中的實例是有道翻譯,運行結果如下:

看彈幕說是有道翻譯加了反爬蟲機制,所以自己用百度翻譯做了一個,研究了好一會兒,新手還是懵懵懂懂的,不過做出來了還是很開心的。代碼如下所示:

復制代碼
import urllib.request
import urllib.parse
import json

while True:
    content = input("請輸入需要翻譯的內容(退出q):")

    if content in['q','Q']:
        break
    else:

        url='http://www.meimei169.com/'

        data={}
        data['from'] = 'en'
        data['to'] = 'zh'
        data['query'] = content
        data['transtype'] = 'translang'
        data['simple_means_flag'] = 3

        data = urllib.parse.urlencode(data).encode('utf-8')


        response = urllib.request.urlopen(url,data)
        html = response.read().decode('utf-8')
        target = json.loads(html)
        print("翻譯結果為:%s"%(target['trans_result']['data'][0]['dst']))
復制代碼

打開翻譯首頁,點擊翻譯,在Network中找打方法為post的項,各個瀏覽器可能有差異,可嘗試在Network里的XHR中查找。

代碼中的url和data是復值表頭中的url和Form Data,在IE瀏覽器中我找了好久,下面分別為360瀏覽器和IE瀏覽器的截圖:

360:

IE:

接着我們解釋此行代碼:

data = urllib.parse.urlencode(data).encode('utf-8')

當data未賦值時,是以GET的方式提交,當data賦值后,POST將會取代GET將數據提交。如上圖所示,data必須基於某一模式,我們使用urllib.parse.urlencode()即可將字符串轉換為需要的模式。

代碼中使用了josen模塊,因為直接打印出html出來的是json格式的數據不利於直接觀看。最終運行結果如下所示:

2 隱藏

為什么要進行隱藏操作?因為如果一個IP在一定時間訪問過於頻繁,那么就會被被訪問網站進行反爬蟲攔截,無法進行我們爬蟲的后續工作了,所以要給爬蟲披上一層神秘的面紗,從而瞞天過海嘍~

兩種方法隱藏(修改)headers:

(1)通過Request的headers參數修改

(2)通過Request.add_header(key,val)方法修改

文檔中說到headers必須是字典的形式,所以方法(1)直接通過增加字典鍵和對應值的方式來進行隱藏,如下所示,找到Request Headers中的User-Agent對應的值進行添加。

復制代碼
import urllib.request
import urllib.parse
import json

while True:
    content = input("請輸入需要翻譯的內容(退出q):")

    if content in['q','Q']:
        break
    else:

        url='http://www.meimei169.com/'

        head = {}
        head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'

        data={}
        data['from'] = 'en'
        data['to'] = 'zh'
        data['query'] = content
        data['transtype'] = 'translang'
        data['simple_means_flag'] = 3

        data = urllib.parse.urlencode(data).encode('utf-8')

        req = urllib.request.Request(url,data,head)
        response = urllib.request.urlopen(req)
        html = response.read().decode('utf-8')
        
        target = json.loads(html)
        print("翻譯結果為:%s"%(target['trans_result']['data'][0]['dst']))
復制代碼

運行結果及headers是否正確輸入的檢查:

請輸入需要翻譯的內容(退出q):love
翻譯結果為:愛
請輸入需要翻譯的內容(退出q):q
>>> req.headers #檢查
{'User-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}

第二種方法:

復制代碼
import urllib.request
import urllib.parse
import json

while True:
    content = input("請輸入需要翻譯的內容(退出q):")

    if content in['q','Q']:
        break
    else:

        url='http://www.meimei169.com/'

##        head = {}
##        head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'

        data={}
        data['from'] = 'en'
        data['to'] = 'zh'
        data['query'] = content
        data['transtype'] = 'translang'
        data['simple_means_flag'] = 3

        data = urllib.parse.urlencode(data).encode('utf-8')

##        req = urllib.request.Request(url,data,head)#替換成下一句,因為不再引用上面的head所以去掉head
        req = urllib.request.Request(url,data)
        #使用add_header(key,value)
        req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36')

        response = urllib.request.urlopen(req)
        html = response.read().decode('utf-8')
        
        target = json.loads(html)
        print("翻譯結果為:%s"%(target['trans_result']['data'][0]['dst']))
復制代碼

第三種方法是引入休息時間,調用time模塊的time.sleep來延長時間以避免網站認為是爬蟲非法訪問。

第四種方法是引入代理,代理把看到的內容返回給你,所以可以達到同樣的效果。使用代理的步驟如下:

1. 參數是一個字典 {‘類型’:‘代理ip:端口號’}

proxy_support = urllib.request.ProxyHandler({})

2. 定制、創建一個 opener

opener = urllib.request.build_opener(proxy_support)

3a. 安裝 opener
urllib.request.install_opener(opener)

3b. 調用 opener

opener.open(url)

復制代碼
import urllib.request

url='http://www.meimei169.com/'
##iplist=['']

proxy_support = urllib.request.ProxyHandler({'http':'115.46.123.180:8123'})
#proxy_support = urllib.request.ProxyHandler({'http':random.choice(iplist)})

opener=urllib.request.build_opener(proxy_support)
opener.addheaders=[('User-Agent','Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36')]
urllib.request.install_opener(opener)

response=urllib.request.urlopen(url)
html=response.read().decode('utf-8')

print(html)
復制代碼

運行結果如下所示,返回的IP地址是你的代理IP地址。

3 爬蟲抓取煎蛋妹子圖

跟着小甲魚的視頻去煎蛋網抓取妹子圖啦,下述內容將自動進行和諧咔咔咔...

思路:新建本地保存圖片文件夾→打開網站→記住圖片的地址→保存圖片到相應的文件夾

如圖為煎蛋網妹子圖網頁顯示,圖片是按照頁碼來放置的。

我們發現點擊不同的頁碼,url改變的只是頁碼處的數字。

http://jandan.net/ooxx/page-190#comments

首先我們要獲取頁碼,在頁碼處右鍵點擊審查元素,如下所示:

則我們可以讀取到網頁的html,然后使用find函數來找到[190]中的數字190,也就是當前頁碼。

接着我們要獲取當前頁碼下每張圖片的url,同樣在圖片點擊右鍵選擇審查元素,可看到圖片的地址如下:

嘻嘻,是gakki。以上是准備工作,接着我們就可以寫出大概的框架來,其余的內容由函數封裝實現

復制代碼
def download_mm(folder = 'ooxx',pages = 10):
    os.mkdir(folder)
    os.chdir(folder)
    
    url='http://www.meimei169.com/'
    page_num = int(get_page(url))

    for i in range(pages):
        page_num -= i
        page_url = url + 'page-' + str(page_num) + '#comments'
        img_addrs = find_imgs(page_url)
        save_imgs(folder,img_addrs)
復制代碼

完整實現代碼如下所示:

復制代碼
import urllib.request
import os

def url_open(url):
    req = urllib.request.Request(url)
    req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36')

    response = urllib.request.urlopen(req)
    html = response.read()

    return html

def get_page(url):
    html = url_open(url).decode('utf-8')

    a = html.find('current-comment-page') + 23
    b = html.find(']',a)
    
    return html[a:b]
    
def find_imgs(url):
    html = url_open(url).decode('utf-8')
    img_addrs = []

    a = html.find('img src=')
    while a != -1:
        b = html.find('.jpg',a,a+255)
        if b != -1:
            img_addrs.append(html[a+9:b+4])
        else:
            b = a + 9
        a = html.find('img src=',b)

    return img_addrs

def save_imgs(folder,img_addrs):
    for each in img_addrs:
        filename = each.split('/')[-1]
        with open(filename, 'wb') as f:
            img = url_open("http:"+each)
            f.write(img)
    
def download_mm(folder = 'ooxx',pages = 10):
    os.mkdir(folder)
    os.chdir(folder)
    
    url='http://www.meimei169.com/'
    page_num = int(get_page(url))

    for i in range(pages):
        page_num -= i
        page_url = url + 'page-' + str(page_num) + '#comments'
        img_addrs = find_imgs(page_url)
        save_imgs(folder,img_addrs)

if __name__ =='__main__':
    download_mm()
復制代碼

成功在本地新建的文件夾中獲取到了jpg的圖片。

4 異常處理

(1)URLError

當urlopen無法處理一個響應的時候,就會引發URLError異常。 通常,沒有網絡連接或者對方服務器壓根兒不存在的情況下,就會引發這個異常。同時,這個URLError會伴隨一個reason屬性,用於包含一個由錯誤編碼和錯誤信息組成的元組。

(2)HTTPError

HTTPError是URLError的子類,服務器上每一個HTTP的響應都包含一個數字的“狀態碼”。有時候狀態碼會指出服務器無法完成的請求類型,一般情況下Python會幫你處理一部分這類響應(例如,響應的是一個“重定向”,要求客戶端從別的地址來獲取文檔,那么urllib會自動為你處理這個響應。);但是呢,有一些無法處理的,就會拋出HTTPError異常。這些異常包括典型的:404(頁面無法找到),403(請求禁止)和401(驗證請求)。

下述舉例說明Python處理異常的兩種方法:

復制代碼
from urllib.request import Request,urlopen
from urllib.error import URLError,HTTPError
req = Request(someurl)
try:
    response = urlopen(req)
except HTTPError as e:
    print('The server coudln\'t fulfill the request.')
    print('Error code:',e.code)
except URLError as e:
    print('We failed to reach a server.')
    print('Reason:',e.reason)
else:
    #do something
復制代碼

 

注意HTTPError要在URLError前面。

復制代碼
from urllib.request import Request,urlopen
from urllib.error import URLError,HTTPError
req = Request(someurl)
try:
    response = urlopen(req)
except HTTPError as e:
    if hasattr(e,'reason')
        print('We failed to reach a server.')
        print('Reason:',e.reason)
    elif hasattr(e,'code'):
        print('The server coudln\'t fulfill the request.')
        print('Error code:',e.code)
else:
    #do something
復制代碼

 


免責聲明!

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



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