好的生活就是不瞎想麦锯,做得多,要得少琅绅,常微笑懂知足!
總結:
- HTTP 是基于 socket 通訊的扶欣;是 異步請求;
- data參數(shù)決定是GET還是POST請求:為空采用GET方法千扶;不為空采用POST方法料祠;
- 對URL打包,對數(shù)據(jù)進行解析澎羞;
1. 概述
當今大數(shù)據(jù)的時代髓绽,網(wǎng)絡爬蟲已經(jīng)成為了獲取數(shù)據(jù)的一個重要手段。
爬蟲妆绞,應該稱為網(wǎng)絡爬蟲顺呕,也叫網(wǎng)頁蜘蛛、網(wǎng)絡機器人括饶、網(wǎng)絡螞蟻等株茶。
搜索引擎,就是網(wǎng)絡爬蟲的應用者巷帝。
為什么到了今天忌卤,反而這個詞匯被頻繁的提起呢?有搜索引擎不就夠了嗎楞泼?
實際上驰徊,大數(shù)據(jù)時代的到了,所有的企業(yè)都希望通過海量數(shù)據(jù)發(fā)現(xiàn)其中的價值堕阔。
所以棍厂,需要爬取對特定網(wǎng)站、特定類別的數(shù)據(jù)超陆,而搜索引擎不能提供這樣的功能牺弹,因此浦马,需要自己開發(fā)爬蟲來解
決。
2. 爬蟲分類
通用爬蟲
常見就是搜索引擎张漂,無差別的收集數(shù)據(jù)晶默、存儲,提取關鍵字航攒,構建索引庫磺陡,給用戶提供搜索接口。
爬取一般流程
1.初始一批URL漠畜, 將這些URL放到待爬取隊列
2.從隊列取出這些URL币他, 通過DNS解析IP, 對IP對應的站點下載HTML頁面憔狞, 保存到本地服務器中蝴悉, 爬取完的
URL放到已爬取隊列。
3.分析這些網(wǎng)頁內(nèi)容瘾敢, 找出網(wǎng)頁里面的其他關心的URL鏈接拍冠, 繼續(xù)執(zhí)行第2步, 直到爬取條件結束廉丽。搜索引擎如何獲取一個新網(wǎng)站的URL
● 新網(wǎng)站主動提交給搜索引擎
● 通過其它網(wǎng)站頁面中設置的外鏈
● 搜索引擎和DNS服務商合作倦微, 獲取最新收錄的網(wǎng)站
聚焦爬蟲
有針對性的編寫特定領域數(shù)據(jù)的爬取程序,針對某些類別數(shù)據(jù)采集的爬蟲正压,是面向主題的爬蟲
3. Robots協(xié)議(原則)
指定一個robots.txt文件欣福,告訴爬蟲引擎什么可以爬取。
/ 表示網(wǎng)站根目錄焦履,表示網(wǎng)站所有目錄拓劝。
Allow 允許爬取的目錄
Disallow 禁止爬取的目錄
可以使用通配符
淘寶 http://www.taobao.com/robots.txt
馬蜂窩 http://www.mafengwo.cn/robots.txt
User-agent: Baiduspider
Allow: /article
Allow: /oshtml
Allow: /ershou
Disallow: /product/
Disallow: /
User-Agent: Googlebot
Allow: /article
Allow: /oshtml
Allow: /product
Allow: /spu
Allow: /dianpu
Allow: /oversea
Allow: /list
Allow: /ershou
Disallow: /
User-agent: Bingbot
Allow: /article
Allow: /oshtml
Allow: /product
Allow: /spu
Allow: /dianpu
Allow: /oversea
Allow: /list
Allow: /ershou
Disallow: /
User-agent: Baiduspider
Allow: /article
Allow: /oshtml
Allow: /ershou
Disallow: /product/
Disallow: /
其它爬蟲,不允許爬取
User-Agent: *
Disallow: /
這是一個君子協(xié)定嘉裤,“爬亦有道”
這個協(xié)議為了讓搜索引擎更有效率搜索自己內(nèi)容郑临,提供了如Sitemap這樣的文件。Sitemap往往是一個XML文件屑宠,
提供了網(wǎng)站想讓大家爬取的內(nèi)容的更新信息厢洞。
這個文件禁止抓取的往往又是可能我們感興趣的內(nèi)容,它反而泄露了這些地址典奉。
301躺翻,302
301,302 都是HTTP狀態(tài)的編碼卫玖,都代表著某個URL發(fā)生了轉移公你,不同之處在于:
301 redirect: 301 代表永久性轉移(Permanently Moved)。
302 redirect: 302 代表暫時性轉移(Temporarily Moved )假瞬。
4. HTTP請求和響應處理
其實爬取網(wǎng)頁就是通過HTTP協(xié)議訪問網(wǎng)頁陕靠,不過通過瀏覽器訪問往往是人的行為迂尝,把這種行為變成使用程序來訪問。
4.1 urllib包
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包寸认。
1. urllib.request模塊
模塊定義了在基本和摘要式身份驗證、 重定向串慰、 cookies等應用中打開 Url (主要是 HTTP) 的函數(shù)和類偏塞。
urlopen方法
urlopen(url, data=None)
url是鏈接地址字符串,或請求類的實例邦鲫。
data提交的數(shù)據(jù)灸叼,如果data為None發(fā)起GET請求,否則發(fā)起POST請求庆捺。見 urllib.request.Request#get_method
返回http.client.HTTPResponse類的響應對象古今,這是一個類文件對象。
from urllib import request
from http.client import HTTPResponse
url = "https://cn.bing.com/"
response:HTTPResponse = request.urlopen(url) # 類文件對象滔以;
with response:
print(1,type(response)) # 類文件對象
print(2,response.headers) # 等價 info () #
print(2,response.info()) # 等價 info () #
print(3,response.geturl())
print(4,response.status,response.reason)
print(5,response.read()) # 二進制的bytes對象
#------------------------------------------------------------------------------
C:\ProgramData\Miniconda3\envs\blog\python.exe C:/Users/dell/PycharmProjects/spiders/t1.py
1 <class 'http.client.HTTPResponse'>
2 Cache-Control: private, max-age=0
Content-Length: 111838
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
P3P: CP="NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND"
Set-Cookie: SRCHD=AF=NOFORM; domain=.bing.com; expires=Fri, 26-Aug-2022 02:43:09 GMT; path=/
Set-Cookie: SRCHUID=V=2&GUID=ECC0C843F1BA4EE7A8D5557520066A17&dmnchg=1; domain=.bing.com; expires=Fri, 26-Aug-2022 02:43:09 GMT; path=/
Set-Cookie: SRCHUSR=DOB=20200826; domain=.bing.com; expires=Fri, 26-Aug-2022 02:43:09 GMT; path=/
Set-Cookie: _SS=SID=2FDBF51342FC674231D5FA2143D266D4; domain=.bing.com; path=/
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-MSEdge-Ref: Ref A: 1E707EB1AAB64DB69C0FD272FD61B7BD Ref B: BJ1EDGE0221 Ref C: 2020-08-26T02:43:09Z
Set-Cookie: _EDGE_S=F=1&SID=2FDBF51342FC674231D5FA2143D266D4; path=/; httponly; domain=bing.com
Set-Cookie: _EDGE_V=1; path=/; httponly; expires=Mon, 20-Sep-2021 02:43:09 GMT; domain=bing.com
Set-Cookie: MUID=3AC183D8072E6AED16758CEA06006BDE; samesite=none; path=/; secure; expires=Mon, 20-Sep-2021 02:43:09 GMT; domain=bing.com
Set-Cookie: MUIDB=3AC183D8072E6AED16758CEA06006BDE; path=/; httponly; expires=Mon, 20-Sep-2021 02:43:09 GMT
Date: Wed, 26 Aug 2020 02:43:08 GMT
Connection: close
上例捉腥,通過urllib.request.urlopen方法,發(fā)起一個HTTP的GET請求你画,WEB服務器返回了網(wǎng)頁內(nèi)容抵碟。響應的數(shù)據(jù)被
封裝到類文件對象中,
通過read方法坏匪、readline方法拟逮、readlines方法獲取數(shù)據(jù),
status和reason屬性表示返回的狀態(tài)碼适滓,
info方法返回頭信息敦迄,
User-Agent問題
上例的代碼非常精簡,即可以獲得網(wǎng)站的響應數(shù)據(jù)凭迹。但目前urlopen方法通過url字符串和data發(fā)起HTTP的請求罚屋。
如果想修改HTTP頭,例如useragent蕊苗,就得借助其他方式沿后。
源碼中構造的useragent如下
# urllib.request.OpenerDirector
class OpenerDirector:
def __init__(self):
client_version = "Python-urllib/%s" % __version__
self.addheaders = [('User-agent', client_version)]
當前顯示為 Python-urllib/3.6
有些網(wǎng)站是反爬蟲的,所以要把爬蟲偽裝成瀏覽器朽砰。隨便打開一個瀏覽器尖滚,復制瀏覽器的UA值喉刘,用來偽裝。
Request類
Request(url, data=None, headers={})
初始化方法漆弄,構造一個請求對象睦裳。可添加一個header的字典撼唾。data參數(shù)決定是GET還是POST請求廉邑。
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
]
ua=random.choice(ua_list) #pick one
#ua需要加到請求頭中
request=Request(url)
request.add_header('User-Agent', random.choice(ua_list) )
print(type(request) )
response = urlopen(request, timeout=20) # request對象或者url都可以
print(type(response))
with response:
print(1, response.status, response.getcode(), response.reason) # 狀態(tài)倒谷,getcode本質(zhì)上就是返回 status
print(2, response.geturl()) # 返回數(shù)據(jù)的url蛛蒙。如果重定向,這個url和原始url不一樣
# 例如原始url是http://www.bing.com/渤愁,返回http://cn.bing.com/
print(3, response.info()) # 返回響應頭headers
print(4, response.read()) # 讀取返回的內(nèi)容
print(5, request.get_header('User-agent'))
print(6, 'user-agent'.capitalize())
2.urllib.parse模塊
該模塊可以完成對url的編解碼; 先看一段代碼牵祟,進行編碼(默認UTF-8)
from urllib import parse
# urlencode函數(shù)第一參數(shù)要求是一個字典或者二元組序列
u = parse.urlencode({'url':'http://www.magedu.com/python'})
print(u)
#---------------------------------------------------
url=http%3A%2F%2Fwww.magedu.com%2Fpython
/ %2F
= 3D
從運行結果來看冒號、斜杠抖格、&诺苹、等號、問號等符號全部被編碼了雹拄,%之后實際上是單字節(jié)十六進制表示的值收奔。
一般來說url中的地址部分,一般不需要使用中文路徑滓玖,但是參數(shù)部分坪哄,不管GET還是POST方法,提交的數(shù)據(jù)中呢撞,
可能有斜桿损姜、等號、問號等符號殊霞,這樣這些字符表示數(shù)據(jù)摧阅,不表示元字符。如果直接發(fā)給服務器端绷蹲,就會導致接收
方無法判斷誰是元字符棒卷,誰是數(shù)據(jù)了。
為了安全祝钢,一般會將數(shù)據(jù)部分的字符做url編碼比规,這樣就不會有歧義了。
后來可以傳送中文拦英,同樣會做編碼蜒什,一般先按照字符集的encoding要求轉換成字節(jié)序列,每一個字節(jié)對應的十六
進制字符串前加上百分號即可疤估。
# 網(wǎng)頁使用utf-8編碼
# https://www.baidu.com/s?wd=中
# 上面的url編碼后灾常,如下
# https://www.baidu.com/s?wd=%E4%B8%AD
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))
------------------------------------------------------
https://www.baidu.com/s?wd=%E4%B8%AD
b'\xe4\xb8\xad'
wd=中
https://www.baidu.com/s?wd=中
4.2 提交方法method
最常用的HTTP交互數(shù)據(jù)的方法是GET霎冯、POST。
GET方法钞瀑,數(shù)據(jù)是通過URL傳遞的沈撞,也就是說數(shù)據(jù)是在HTTP報文的header部分。
POST方法雕什,數(shù)據(jù)是放在HTTP報文的body部分提交的缠俺。
數(shù)據(jù)都是鍵值對形式,多個參數(shù)之間使用&符號連接贷岸。例如a=1&b=abc
from urllib import request,parse
from http.client import HTTPResponse
# ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
base_url = "https://cn.bing.com/search"
u = parse.urlencode({'q':'馬哥教育'})
url = "{}?{}".format(base_url,u)
req = request.Request(url,headers={'User-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"})
with request.urlopen(req) as response:
with open('./bing.html','wb') as f: #注意編碼壹士;
f.write(response.read())
#-------------------------------------------------------
GET方法
連接 必應 搜索引擎官網(wǎng),獲取一個搜索的URL http://cn.bing.com/search?q=馬哥教育
需求 : 請寫程序完成對關鍵字的bing搜索偿警, 將返回的結果保存到一個網(wǎng)頁文件
from urllib import request,parse
from http.client import HTTPResponse
# ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
# 請求什么返回什么
url = "http://httpbin.org/get"
req = request.Request(url,headers={'User-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"})
with request.urlopen(req) as response:
print(response.read())
#------------------------------------------------------------
b'{\n "args": {}, \n "headers": {\n "Accept-Encoding": "identity", \n "Host": "httpbin.org", \n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", \n "X-Amzn-Trace-Id": "Root=1-5f463de0-28f2bea1625061d03c4977d3"\n }, \n "origin": "117.150.188.202", \n "url": "http://httpbin.org/get"\n}\n'
POST方法
http://httpbin.org/ 測試網(wǎng)站
from urllib import request,parse
from http.client import HTTPResponse
# ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
# 請求什么返回什么
url = "http://httpbin.org/post"
req = request.Request(url,headers={'User-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"})
params = parse.urlencode({
'id':100,
'name':'tommy'
})
with request.urlopen(req,params.encode()) as response:
print(response.read())
#- --------------------------------------------------------------
b'{\n "args": {}, \n "headers": {\n "Accept-Encoding": "identity", \n "Host": "httpbin.org", \n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", \n "X-Amzn-Trace-Id": "Root=1-5f463de0-28f2bea1625061d03c4977d3"\n }, \n "origin": "117.150.188.202", \n "url": "http://httpbin.org/get"\n}\n'
處理JSON數(shù)據(jù)
查看"豆瓣電影"墓卦,看到"最近熱門電影"的“熱門” ;
XHR : xml http request
tag 標簽“熱門”户敬,表示熱門電影
type 數(shù)據(jù)類型,movie是電影
page_limit 表示返回數(shù)據(jù)的總數(shù)
page_start 表示數(shù)據(jù)偏移
通過分析睁本,我們知道這部分內(nèi)容尿庐,是通過A JAX從后臺拿到的Json數(shù)據(jù)。
訪問URL是https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&page_limit=50&page_start=0%E7%83%AD%E9%97%A8
是utf-8編碼的中文“熱門” 服務器返回的Json數(shù)據(jù)50條
from urllib import request,parse
from http.client import HTTPResponse
# ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
# 請求什么返回什么
url = "https://movie.douban.com/j/search_subjects"
params = parse.urlencode({
'type':'movie',
'tag':'熱門',
'page_limit':3,
'page_start':5,
})
url += '?' + params
req = request.Request(url,headers={'User-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"})
with request.urlopen(req) as response:
print(response.read())
-----------------------------------------------
b'{"subjects":[{"rate":"8.3","cover_x":5906,"title":"\xe5\xb0\x91\xe5\xb9\xb4\xe7\x9a\x84\xe4\xbd\xa0","url":"https:\\/\\/movie.douban.com\\/subject\\/30166972\\/","playable":true,"cover":"https://img3.doubanio.com\\/view\\/photo\\/s_ratio_poster\\/public\\/p2572166063.jpg","id":"30166972","cover_y":8268,"is_new":false},{"rate":"5.6","cover_x":1500,"title":"\xe8\xb6\x85\xe8\x83\xbd\xe8\xae\xa1\xe5\x88\x92","url":"https:\\/\\/movie.douban.com\\/subject\\/30330875\\/","playable":false,"cover":"https://img9.doubanio.com\\/view\\/photo\\/s_ratio_poster\\/public\\/p2614190404.jpg","id":"30330875","cover_y":2222,"is_new":false},{"rate":"6.2","cover_x":1080,"title":"\xe5\xa6\x99\xe5\x85\x88\xe7\x94\x9f","url":"https:\\/\\/movie.douban.com\\/subject\\/34888476\\/","playable":true,"cover":"https://img9.doubanio.com\\/view\\/photo\\/s_ratio_poster\\/public\\/p2614234255.jpg","id":"34888476","cover_y":1512,"is_new":false}]}'