爬蟲-1.概述和HTTP請求與響應處理

爬蟲-1.概述和HTTP請求與響應處理

概述

  • 爬蟲,應該稱爲網絡爬蟲,也叫網頁蜘蛛、網絡機器人、網絡螞蟻等。
  • 搜索引擎,就是網絡爬蟲的應用者。
  • 大數據時代的到來,所有企業都希望通過海量數據發現其中的價值。所以需要爬取對特定網站、特頂類別的數據,而搜索引擎不能提供這樣的功能,因此需要自己開發爬蟲來解決。

爬蟲分類

1.通用爬蟲

常見就是搜索引擎,無差別的蒐集數據、存儲、提取關鍵字、構建索引庫,給用戶提供搜索接口。

  • 爬取一般流程
    1. 初始化一批URL,將這些URL放到帶爬隊列
    2. 從隊列取出這些URL,通過DNS解析IP,對IP對應的站點下載HTML頁面,保存到本地服務器中,爬取完的URL放到已爬取隊列。
    3. 分析這些網頁內容,找出網頁裏面的其他關心的URL鏈接,繼續執行第2步,直到爬取條件結束。
  • 搜索引擎如何獲取一個網站的URL
    1. 新網站主動提交給搜索引擎
    2. 通過其他網站頁面中設置的外鏈接
    3. 搜索引擎和DNS服務商合作,獲取最新收錄的網站

2. 聚焦爬蟲

  • 有針對性的編寫特定領域數據的爬取程序,針對 某些類別數據採集的爬蟲,是面向主題的爬蟲

Robots協議

指定一個robots.txt文件,告訴爬蟲引擎什麼可以爬取

  • /表示網站根目錄,表示網站所有目錄。
  • Allow允許爬取的目錄
  • Disallow禁止爬取的目錄
  • 可以使用通配符

robots是一個君子協定,“爬亦有道”
這個協議爲了讓搜索引擎更有效率搜索自己內容,提供了Sitemap這樣的文件。Sitemap往往是一個XML文件,提供了網站想讓大家爬取的內容的更新信息。
這個文件禁止爬取的往往又是可能我們感興趣的內容,反而泄露了這些地址。

  1. 示例:淘寶的robotshttp://www.taobao.com/robots.txt

    User-agent:  Baiduspider
    Allow:  /article
    Allow:  /oshtml
    Allow:  /ershou
    Allow: /$
    Disallow:  /product/
    Disallow:  /
    
    User-Agent:  Googlebot
    Allow:  /article
    Allow:  /oshtml
    Allow:  /product
    Allow:  /spu
    Allow:  /dianpu
    Allow:  /oversea
    Allow:  /list
    Allow:  /ershou
    Allow: /$
    Disallow:  /
    
    User-agent:  Bingbot
    Allow:  /article
    Allow:  /oshtml
    Allow:  /product
    Allow:  /spu
    Allow:  /dianpu
    Allow:  /oversea
    Allow:  /list
    Allow:  /ershou
    Allow: /$
    Disallow:  /
    
    User-Agent:  360Spider
    Allow:  /article
    Allow:  /oshtml
    Allow:  /ershou
    Disallow:  /
    
    User-Agent:  Yisouspider
    Allow:  /article
    Allow:  /oshtml
    Allow:  /ershou
    Disallow:  /
    
    User-Agent:  Sogouspider
    Allow:  /article
    Allow:  /oshtml
    Allow:  /product
    Allow:  /ershou
    Disallow:  /
    
    User-Agent:  Yahoo!  Slurp
    Allow:  /product
    Allow:  /spu
    Allow:  /dianpu
    Allow:  /oversea
    Allow:  /list
    Allow:  /ershou
    Allow: /$
    Disallow:  /
    
    User-Agent:  *
    Disallow:  /
    
  2. 示例馬蜂窩tobotshttp://www.mafengwo.cn/robots.txt

    User-agent: *
    Disallow: /
    Disallow: /poi/detail.php
    
    Sitemap: http://www.mafengwo.cn/sitemapIndex.xml
    

HTTP請求和響應處理

其實爬取網頁就是通過HTTP協議訪問網頁,不過通過瀏覽器反問往往是人的行爲,把這種行爲變成使用程序來訪問。

urllib包

urllib是標準庫,它一個工具包模塊,包含下面模塊來處理url:
* urllib.request 用於打開和讀寫url
* urllib.error 包含了由urllib.request引起的異常
* urllib.parse 用於解析url
* urllib.robotparser 分析robots.txt文件

Python2中提供了urllib和urllib2。urllib提供較爲底層的接口,urllib2對urllib進行了進一步封裝。Python3中將urllib合併到了urllib2中,並更名爲標準庫urllib包。

urllib.request模塊

定義了在基本和摘要式身份驗證、重定向、cookies等應用中打開Url(主要是HTTP)的函數和類。

  • urlopen方法

    1. urlopen(url,data=None)
      • url是鏈接地址字符串,或請求類的實例
      • data提交的數據,如果data爲Non發起的GET請求,否則發起POST請求。見urllib.request.Request#get_method返回http.client.HTTPResponse類的相遇對象,這是一個類文件對象。
    from urllib.request import urlopen
    
    # 打開一個url返回一個相應對象,類文件對象
    # 下面鏈接訪問後會有跳轉
    responses = urlopen("http://www.bing.com") #默認GET方法
    print(responses.closed)
    with responses:
        print(1, type(responses)) # http.client.HTTPResponse類文件對象
        print(2,responses.status,responses.reason) #狀態
        print(3,responses.geturl()) #返回真正的URL
        print(4,responses.info()) #headers
        print(5,responses.read()[:50]) #讀取返回的內容
    
    print(responses.closed)
    

001
1. 上例,通過urllib.request.urlopen方法,發起一個HTTP的GET請求,WEB服務器返回了網頁內容。響應的數據被封裝到類文件對象中,可以通過read方法、readline方法、readlines方法獲取數據,status和reason屬性表示返回的狀態碼,info方法返回頭信息,等等。

  • User-Agent問題

    1. 上例代碼非常精簡,即可以獲得網站的響應數據。但目前urlopen方法通過url字符串和data發起HTTP的請求。如果想修改HTTP頭,例如useragent,就的藉助其他方式。

      • 原碼中構造的useragen如下:
      # urllib.request.OpenerDirector
      class OpenerDirector:
          def __init__(self):
              client_version = "Python-urllib/%s" % __version__
              self.addheaders = [('User-agent', client_version)]
      ```
      * 當前顯示爲Python-urlib/3.7
      * 有些網站是反爬蟲的,所以要把爬蟲僞裝成瀏覽器。順便打開一個瀏覽器,複製李立羣的UA值,用來僞裝。
      
      
  • Request類
    Request(url,data=None,headers={})
    初始化方法,構造一個請求對象。可添加一個header的字典。data參數決定是GET還是POST請求。
    obj.add_header(key,val)爲header增加一個鍵值對。

from urllib.request import Request,urlopen
import random

# 打開一個url返回一個Request請求對象
# url = "https://movie.douban.com/" #注意尾部的斜槓一定要有
url = "http://www.bing.com/"

ua_list = [
    "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36", # chrome
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0.1 Safari/537.36", # safafi
    "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0", # Firefox
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" # IE
]

ua = random.choice(ua_list)
request = Request(url)
request.add_header("User-Agent",ua)
print(type(request))

response = urlopen(request,timeout=20) #request對象或者url都可以
print(type(response))

with response:
    print(1,response.status,response.getcode(),response.reason) #狀態,getcode本質上就是返回status
    print(2,response.geturl()) #返回數據的url。如果重定向,這個url和原始url不一樣
    # 例如:原始url是http://www.bing.com/,返回http://cn.bing.com/
    print(3,response.info()) #返回響應頭headers
    print(4,response.read()[:50]) #讀取返回的內容

print(5,request.get_header("User-agent"))
print(6,request.headers)
print(7,"user-agent".capitalize())

在這裏插入圖片描述

urllib.parse模塊

該模塊可以完成對url的編解碼

  1. parse.urlencode({key:value}) #對查詢字符串進行編碼
from urllib import parse

u = parse.urlencode({
    "url":"http://www.xdd.com/python",
    "p_url":"http://www.xdd.com/python?id=1&name=張三"
})
print(u)

# 運行結果
url=http%3A%2F%2Fwww.xdd.com%2Fpython&p_url=http%3A%2F%2Fwww.xdd.com%2Fpython%3Fid%3D1%26name%3D%E5%BC%A0%E4%B8%89

從運行結果來看冒號、斜槓、&、等號、問號等符號全部被編碼了,%之後實際上是單字節十六進制表示的值。
一般來說url中的地址部分,一般不需要使用中文路徑,但是參數部分,不管GET還是POST方法,提交的數據中, 可能有斜杆、等號、問號等符號,這樣這些字符表示數據,不表示元字符。如果直接發給服務器端,就會導致接收 方無法判斷誰是元字符,誰是數據了。爲了安全,一般會將數據部分的字符做url編碼,這樣就不會有歧義了。 後來可以傳送中文,同樣會做編碼,一般先按照字符集的encoding要求轉換成字節序列,每一個字節對應的十六 進制字符串前加上百分號即可。

from urllib import parse

u = parse.urlencode({"wd":"中"}) #編碼查詢字符串
url= "https://www.baidu.com/s?{}".format(u)
print(url)

print("中".encode("utf-8")) # b'xe4\xb8\xad'
print(parse.unquote(u)) #解碼
print(parse.unquote(url))

在這裏插入圖片描述

提交方法method

  • 常用的HTTP交互數據的方法是GET、POST
    1. GET方法,數據是通過URL傳遞的,也就是說數據是在HTTP報文的header部分。
    2. POST方法,數據是放在HTTP報文的body部分提交的。
    3. 數據是鍵值對形式,多個參數質檢使用&符號鏈接。例如a=1&b=abc

GET方法

  • 鏈接必應搜索引擎官網,獲取一個搜索的URLhttp://cn.bing.com/search?q=神探狄仁傑
from urllib.request import urlopen,Request
from urllib.parse import urlencode

data = urlencode({"q":"神探狄仁傑"})
base_url = "http://cn.bing.com/search"
url = "{}?{}".format(base_url,data)
safafi = "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0.1 Safari/537.36" # safafi

request = Request(url,headers={"User-agent":safafi})
repost = urlopen(request)
with repost:
    with open("d:/abc.html","wb") as f:
        f.write(repost.read())
print("ok")

POST方法

from urllib.request import Request,urlopen
from urllib.parse import urlencode
import simplejson

request = Request("http://httpbin.org/post")
request.add_header("User-agent","Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0.1 Safari/537.36")
data = urlencode({"name":"張三,@=/&","age":"6"})
print(data)

res = urlopen(request,data.encode()) #POST方法,Form提價數據,如果Data的值不是None就使用Post方法,否則Get方法
with res:
    j = res.read().decode() #json
    print(j)
    print("===============================")
    print(simplejson.loads(j))

在這裏插入圖片描述

處理JSON數據

005

  • 查看“豆瓣電影”,中的熱門電影,通過分析,我們知道這部分內容,是通過AJAX從後臺拿到的JSON數據。
  • 訪問URL是https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&page_limit=50&page_start=0
    1. %E7%83%AD%E9%97%A8是utf-8編碼的中文"熱門"
    2. tag 標籤"熱門",表示熱門電影
    3. type 數據類型,movie是電影
    4. page_limit表示返回數據的總數
    5. page_start 表示數據偏移
  • 服務器返回json數據如下:(輪播組件,共50條數據)
    006
from urllib.request import Request,urlopen
from urllib.parse import urlencode

base_url = "https://movie.douban.com/j/search_subjects"
data = urlencode({
    "tag":"熱門",
    "type":"movie",
    "page_limit":10,
    "page_start":10
})
request = Request(base_url)

# POST方法
repost = urlopen(request,data=data.encode())
with repost:
    print(repost._method)
    print(repost.read().decode()[:100])

# GET方法
with urlopen("{}?{}".format(base_url,data)) as res:
    print(res._method)
    print(res.read().decode()[:100])

007

HTTPS證書忽略

  • HTTPS使用SSL安全套層協議,在傳輸層對網絡數據進行加密。HTTPS使用的時候需要證書,而證書需要CA認證。

  • CA(Certificate Authority)是數字證書認證中心的簡稱,是指發放、管理、廢除數字證書的機構。

  • CA是受信任的第三方,有CA簽發的證書具有可信任。如果用戶由於信任了CA簽發的證書導致的損失,可以追究CA的法律責任。

  • CA是層級結構,下級CA信任上級CA,且有上級CA頒發給下級CA證書並認證。

  • 一些網站,例如淘寶,使用HTTPS加密數據更加安全。

  • 以前舊版本12306網站需要下載證書

from urllib.request import Request,urlopen

# request = Request("http://www.12306.cn/mormhweb/") #可以訪問
# request = Request("https://www.baidu.com/") #可以訪問

request = Request("https://www.12306.cn/mormhweb/") #舊版本報SSL認證異常
request.add_header(
    "User-agent",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0.1 Safari/537.36"
)

# ssl.CertificateError: hostname 'www.12306.cn' doesn't match either of ......
with urlopen(request) as res:
    print(res._method)
    print(res.read())
  • 注意:一下說明都是針對舊版本的12306網站,來講解,現在實在無法找打第二個自己給自己發證書的。
  • 通過HTTPS訪問12306的時候,失敗的原因在於12306的證書未通過CA認證,它是自己生產的證書,不可信。而其它網站訪問,如https://www.baidu.com/並沒有提示的原因,它的證書的發行者受信任,且早就存儲在當前系統中。
  • 遇到這種問題,解決思路:忽略證書不安全信息
from urllib.request import Request,urlopen
import ssl #導入ssl模塊


# request = Request("http://www.12306.cn/mormhweb/") #可以訪問
# request = Request("https://www.baidu.com/") #可以訪問

request = Request("https://www.12306.cn/mormhweb/") #舊版本報SSL認證異常
request.add_header(
    "User-agent",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0.1 Safari/537.36"
)

# 忽略不信任的證書
context = ssl._create_unverified_context()
res = urlopen(request,context=context)

# ssl.CertificateError: hostname 'www.12306.cn' doesn't match either of ......
with res:
    print(res._method)
    print(res.geturl())
    print(res.read().decode())

urllib3庫

https://urllib3.readthedocs.io/en/latest/
標準庫urlib缺少了一些關鍵的功能,非標準庫的第三方庫urllib3提供了,比如說連接池管理。

  • 安裝 pip install urlib3
import urllib3
from urllib3.response import HTTPResponse

url = "https://movie.douban.com"
ua = "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0.1 Safari/537.36"

# 鏈接池管理
with urllib3.PoolManager() as http:
    response:HTTPResponse = http.request("GET",url,headers={"User-Agent":ua})
    print(type(response))
    print(response.status,response.reason)
    print(response.headers)
    print(response.data[:50])

robots_008

requests庫

  • requests使用了urllib3,但是API更加友好,推薦使用。
  • 安裝pip install requests
import requests

ua = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"
url = "https://movie.douban.com/"

response = requests.request("GET",url,headers={"User-Agent":ua})

with response:
    print(type(response))
    print(response.url)
    print(response.status_code)
    print(response.request.headers) #請求頭
    print(response.headers) #響應頭
    response.encoding = "utf-8"
    print(response.text[:200]) #HTML的內容
    with open('d:/movie.html',"w",encoding='utf-8') as f:
        f.write(response.text)
  • requests默認使用Session對象,是爲了多次和服務器端交互中保留會話的信息,例如:cookie。
#直接使用Session
import requests

ua = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0"
urls = ["https://www.baidu.com/s?wd=xdd","https://www.baidu.com/s?wd=xdd"]

session = requests.Session()
with session:
    for url in urls:
        response = session.get(url,headers = {"User-Agent":ua})
        # response = requests.request("GET",url,headers={"User-Agent":ua}) #觀察兩種方式區別
        with response:
            print(response.request.headers) #請求頭
            print(response.cookies) #響應的cookie
            print(response.text[:20]) #HTML的內容
            print("-"*30)
  • 使用session訪問,第二次帶上了cookie

009

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章