Python爬蟲之Requests和Response

http header 消息通常被分為4個部分:general? header即頭部, request header即請求報文, response header即響應(yīng)報文, entity header實體報文。HTTP部分看我的《圖解HTTP》復(fù)習(xí),另外這地址?也很全了倔韭。

本文章分為介紹http的Requests和response常用的字段,以及怎么解析文件類型(怎么網(wǎng)頁徒扶,怎么解析Jason识腿,怎么解析csv)。怎么回調(diào)和保存到csv患蹂,爬蟲陷阱和解決方案等我還沒看夭禽,有時間再更新吧霞掺。


Python中Requests的官方文檔見:這里?

Requests部分:


Responses 部分:


scrapy中的Requests和Response對象

Request請求方法:

scrapy.http.Request(url[, callback, method, headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback])

其中calback是回調(diào)函數(shù),headers是頭部信息驻粟,
包含cookie的發(fā)起請求寫法:

request_with_cookies = Request(url="http://www.example.com", cookies={'name': 'currency', 'value': 'USD', 'domain': 'example.com', 'path': '/currency'}根悼,meta={'dont_merge_cookies': True})


包含回調(diào)函數(shù)的請求寫法:

def parse_page1(self, response):

?????? item = MyItem()

?????? item['main_url'] = response.url

?????? request = scrapy.Request("http://www.example.com/some_page.html",??? callback=self.parse_page2)

?????? request.meta['item'] = item

??????? return request

def parse_page2(self, response):

????? item = response.meta['item']

????? item['other_url'] = response.url

????? return item


表單的請求方法:

from_response(response[, formname=None, formnumber=0, formdata=None, formxpath=None, clickdata=None, dont_click=False, ...])

formnumber:當(dāng)響應(yīng)內(nèi)容包含多個表單的時候用到

表單模擬登陸:

import scrapy

class LoginSpider(scrapy.Spider):

name = 'example.com' start_urls = ['http://www.example.com/users/login.php']

def parse(self, response):

???? return scrapy.FormRequest.from_response( response, formdata={'username': 'john', 'password': 'secret'}, callback=self.after_login )

def after_login(self, response):

# check login succeed before going on

if "authentication failed" in response.body:

??? self.log("Login failed", level=scrapy.log.ERROR)

???? return


Response對象:

屬性有:url, status, headers, body, flags凶异,request,metas挤巡,

文本處理常用方法:

response.body.decode(response.encoding)

response.xpath("**")

response.css("**")



上面是scrapy的部分剩彬,其他通用的內(nèi)容還有:

熟悉請求類型+會話Session+請求頭+請求實體(請求內(nèi)容+參數(shù)等)+響應(yīng)內(nèi)容+響應(yīng)狀態(tài)碼+cookie+重定向與請求歷史+超時處理等。

請求類型:get,post,put,delete等矿卑。

GET與POST方法有以下區(qū)別

(1)?? 在客戶端喉恋,Get方式在通過URL提交數(shù)據(jù),數(shù)據(jù)在URL中可以看到母廷;POST方式轻黑,數(shù)據(jù)放置在HTML HEADER內(nèi)提交。

對于表單的提交方式琴昆,在服務(wù)器端只能用Request.QueryString來獲取Get方式提交來的數(shù)據(jù)氓鄙,用Post方式提交的數(shù)據(jù)只能用Request.Form來獲取

(2)?? GET方式提交的數(shù)據(jù)最多只能有1024 Byte,而POST則沒有此限制业舍。

(3)?? 一般來說抖拦,盡量避免使用Get方式提交表單,因為有可能會導(dǎo)致安全問題舷暮。比如說在登陸表單中用Get方式态罪,用戶輸入的用戶名和密碼將在地址欄中暴露無遺。但是在分頁程序中下面,用Get方式就比用Post好复颈。


會話:持續(xù)跟蹤會話信息,讓發(fā)出的所有請求之間保持 cookie沥割。

模擬瀏覽器耗啦,讓網(wǎng)絡(luò)機器人看起來像人類用戶。

用法:

from requests import Request

Session s = Session()

req = Request('GET', url,

data=data ,? ? ? ? ? ? #data={'key1':'value1','key2':['value2','value3']}

headers=headers? ? #headers={'user-agent':'my-app/0.0.1'}驯遇,模擬瀏覽器

#? files=files ,files = {'file': open('report.xls', 'rb')}? ? ? ? ? ? ? ? ? ?

)

prepped = s.prepare_request(req)

# do something with prepped.body

# do something with prepped.headers

resp = s.send(prepped,

stream=stream,

verify=verify,#驗證 SSL 證書

proxies=proxies,#使用代理proxies={"http":"https":"http://10.10.1.10:1080",}

cert=cert,

timeout=timeout #超時時間

)

print(resp.status_code)

r.headers? #訪問服務(wù)器返回給我們的響應(yīng)頭部信息
r.request.headers? #得到發(fā)送到服務(wù)器的請求的頭部

但是網(wǎng)站用cookie跟蹤你的訪問過程芹彬,如果發(fā)現(xiàn)了爬蟲異常行為就會中斷你的訪問蓄髓,比如特別快速地填寫表單叉庐,或者瀏覽大量頁面。要想正確使用cookie会喝,用PhantomJS方法陡叠。

from selenium import webdriverdriver = webdriver.PhantomJS(executable_path='')

driver.get("http://pythonscraping.com")

driver.implicitly_wait(1)

print(driver.get_cookies())



請求報文的媒體類型有:網(wǎng)頁,jason數(shù)據(jù)肢执,txt文本枉阵,csv文件,表單等预茄。pdf,word兴溜,docx這里不做解釋了侦厚。大家看《python網(wǎng)絡(luò)數(shù)據(jù)采集》這本書,針對每個媒體類型的分析和代碼都有寫拙徽。


解析網(wǎng)頁

find方法刨沦,css選擇器,lxml膘怕,正則表達式想诅。這篇?文章解釋的很清楚了,要好好看暗盒摹@雌啤!

案例1:爬取一個詞條下的所有鏈接和子鏈接忘古。

思路:1)一個函數(shù)getLinks徘禁,可以用維基百科詞條/wiki/< 詞條名稱> 形式的URL 鏈接作為參數(shù),然后以同樣的形式返回一個列表髓堪,里面包含所有的詞條URL 鏈接晌坤。2) 一個主函數(shù),以某個起始詞條為參數(shù)調(diào)用getLinks旦袋,再從返回的URL 列表里隨機選擇一個詞條鏈接骤菠,再調(diào)用getLinks,直到我們主動停止疤孕,或者在新的頁面上沒有詞條鏈接了商乎,程序才停止運行。

完整的代碼如下所示:

from urllib.request import urlopen

from bs4 import BeautifulSoup

import datetime

import random

import re

random.seed(datetime.datetime.now())

def getLinks(articleUrl):

?????? html = urlopen("http://en.wikipedia.org"+articleUrl)

?????? bsObj = BeautifulSoup(html)

????? return bsObj.find("div", {"id":"bodyContent"}).findAll("a",

href=re.compile("^(/wiki/)((?!:).)*$"))

links = getLinks("/wiki/Kevin_Bacon")

while len(links) > 0:

????????? newArticle = links[random.randint(0, len(links)-1)].attrs["href"]

?????????? print(newArticle)

links = getLinks(newArticle)

案例2:訪問鏈接深度少于1000的整個網(wǎng)站

一個常用的費時的網(wǎng)站采集方法就是從頂級頁面開始(比如主頁)祭阀,然后搜索頁面上的所有鏈接鹉戚,形成列表。再去采集這些鏈接的每一個頁面专控,然后把在每個頁面上找到的鏈接形成新的列表抹凳,重復(fù)執(zhí)行下一輪采集。伦腐。在代碼運行時赢底,把已發(fā)現(xiàn)的所有鏈接都放到一起,并保存在方便查詢的列表里(下文示例指Python 的集合set 類型)柏蘑。為了全面地展示這個網(wǎng)絡(luò)數(shù)據(jù)采集示例是如何工作的幸冻,不再限制爬蟲采集的頁面范圍,只有“新”鏈接才會被采集咳焚,之后再從頁面中搜索其他鏈接洽损。只要遇到頁面就查找所有以/wiki/ 開頭的鏈接,也不考慮鏈接是不是包含分號革半。(提示:詞條鏈接不包含分號碑定,而文檔上傳頁面流码、討論頁面之類的頁面URL 鏈接都包含分號。)一開始延刘,用getLinks 處理一個空URL旅掂,其實是維基百科的主頁,因為在函數(shù)里空URL 就是http://en.wikipedia.org访娶。然后商虐,遍歷首頁上每個鏈接,并檢查是否已經(jīng)在全局變量集合pages 里面了(已經(jīng)采集的頁面集合)崖疤。如果不在秘车,就打印到屏幕上,并把鏈接加入pages 集合劫哼,再用getLinks 遞歸地處理這個鏈接叮趴。

from urllib.request import urlopen

from bs4 import BeautifulSoup

import re

pages = set()

def getLinks(pageUrl):

global pages

html = urlopen("http://en.wikipedia.org"+pageUrl)

bsObj = BeautifulSoup(html)

for link in bsObj.findAll("a", href=re.compile("^(/wiki/)")):

??????? if 'href' in link.attrs:

????????????? if link.attrs['href'] not in pages:

???????????????? # 我們遇到了新頁面

???????????????? newPage = link.attrs['href']

???????????????? print(newPage)

??????????????? pages.add(newPage)

??????????????? getLinks(newPage)

? ? ? ? ? ? ? ? getLinks("")

案例3:收集整個網(wǎng)站的鏈接標(biāo)題和正文的第一個段落

和往常一樣,決定如何做好這些事情的第一步就是先觀察網(wǎng)站上的一些頁面权烧,然后擬定一個采集模式眯亦。通過觀察幾個維基百科頁面,包括詞條和非詞條頁面般码,比如隱私策略之類的頁面妻率,就會得出下面的規(guī)則。

from urllib.request import urlopen

from bs4 import BeautifulSoup

import re

pages = set()

def getLinks(pageUrl):

global pages

html = urlopen("http://en.wikipedia.org"+pageUrl)

bsObj = BeautifulSoup(html)

try:

???????? print(bsObj.h1.get_text())

?????? ? print(bsObj.find(id="mw-content-text").findAll("p")[0])

???????? print(bsObj.find(id="ca-edit").find("span").find("a").attrs['href'])

?except AttributeError:

????????? print("頁面缺少一些屬性板祝!不過不用擔(dān)心宫静!")

for link in bsObj.findAll("a", href=re.compile("^(/wiki/)")):

??????? if 'href' in link.attrs:

??????????????? if link.attrs['href'] not in pages:

????????????????????? # 我們遇到了新頁面

???????????????????? newPage = link.attrs['href']

???????????????????? print("----------------\n"+newPage)

???????????????????? pages.add(newPage)

???????????????????? getLinks(newPage)

???????????????????? getLinks("")



解析jason

import json

from urllib.request import urlopen

def getCountry(ipAddress):

response = urlopen("http://freegeoip.net/json/"+ipAddress).read().decode('utf-8')

responseJson = json.loads(response)

return responseJson.get("country_code")

print(getCountry("50.78.253.58"))


讀取文本

1)讀取整個文件:

from urllib.request import urlopen

textPage = urlopen("http://www.pythonscraping.com/pages/warandpeace/chapter1.txt")

?print(textPage.read().rstrip())


2)逐行讀取文件并存入列表:

?from urllib.request import urlopen? ? ? ?

filename='pi_txt"

with open(filename) as file_ob:

?????? lines=file_ob.readline()

for line in lines:

?????? print(textPage.read().rstrip())


讀取CSV:

data = urlopen("http://pythonscraping.com/files/MontyPythonAlbums.csv")

.read().decode('ascii', 'ignore')

dataFile = StringIO(data)

csvReader = csv.reader(dataFile)

for row in csvReader:

? ? ? print(row)? #逐行讀取文件并存入到列表


提交表單

import requests

params = {'firstname': 'Ryan', 'lastname': 'Mitchell'}

r = requests.post("http://pythonscraping.com/files/processing.php", data=params)

print(r.text)


響應(yīng)內(nèi)容

文本:requests.get('https://github.com/timeline.json') >>>r.text

二進制:不寫了,這里券时,官方有孤里。

響應(yīng)狀態(tài)碼

r.status_code,2XX OK橘洞;4XX 客戶端錯誤 捌袜;5XX 服務(wù)器錯誤響應(yīng)

設(shè)置超時時間:timeout=0.001

超時處理:

def? set_timeout(session,*arg)

retryTimes=20;

while retryTimes>0:

???????????? try?

?????????????????????? return session.post(*arg)

??????????? except:

????????????????????? ............

重定向與請求歷史:Response.history

用一種智能的方法來檢測客戶端重定向是否完成, 首先從頁面開始加載時就“ 監(jiān)視”DOM 中的一個元素炸枣, 然后重復(fù)調(diào)用這個元素直到Selenium 拋出一個StaleElementReferenceException 異常虏等;也就是說,元素不在頁面的DOM 里了抛虏,說明這時網(wǎng)站已經(jīng)跳轉(zhuǎn):

from selenium import webdriverimport time

from selenium.webdriver.remote.webelement

import WebElementfrom selenium.common.exceptions

import StaleElementReferenceException

def? waitForLoad(driver):

????????? elem = driver.find_element_by_tag_name("html")

????????? count = 0

????????? while True:

????????????????? count += 1

?????????????????? if count > 20:

?????????????????????????? print("Timing out after 10 seconds and returning")

?????????????????????????? return

????????? ? ??????? time.sleep(.5)

??????????????????? try:

?????????????????????????? elem == driver.find_element_by_tag_name("html")

??????????????????????????? except StaleElementReferenceException:

????????????????????? return

? driver =webdriver.PhantomJS(executable_path='')

? driver.get("http://pythonscraping.com/pages/javascript/redirectDemo1.html")

? waitForLoad(driver)

? print(driver.page_source)


下載數(shù)據(jù)寫入文件

1)專門寫個抓取回調(diào)基類博其,供各個爬蟲方法使用? 2)為鏈接爬蟲添加緩存支持? 3)磁盤緩存 或者? 數(shù)據(jù)庫緩存

這部分沒看 套才,等有時間看吧迂猴。

流程是:調(diào)用open方法并賦予寫入模式w(讀取模式是r,附加模式是a),如果沒有這個文件會自動創(chuàng)建文件然后調(diào)用writte方法寫入數(shù)據(jù)背伴。

filename="wr.txt"

with open(filename,"w") as file_ob:

????? file_ob.write("i love solve questions")


爬蟲陷阱和困難有:

1)用隱含字段阻止網(wǎng)絡(luò)數(shù)據(jù)采集的方式主要有兩種沸毁。第一種是表單頁面上的一個字段可以用服務(wù)器生成的隨機變量表示峰髓。如果提交時這個值不在表單處理頁面上,服務(wù)器就有理由認(rèn)為這個提交不是從原始表單頁面上提交的息尺,而是由一個網(wǎng)絡(luò)機器人直接提交到表單處理頁面的携兵。繞開這個問題的最佳方法就是,首先采集表單所在頁面上生成的隨機變量搂誉,然后再提交到表單處理頁面徐紧。

2)第二種方式是“蜜罐”(honey pot)。如果表單里包含一個具有普通名稱的隱含字段(設(shè)置蜜罐圈套)炭懊,比如“用戶名”(username)或“郵箱地址”(email address)并级,設(shè)計不太好的網(wǎng)絡(luò)機器人往往不管這個字段是不是對用戶可見,直接填寫這個字段并向服務(wù)器提交侮腹,這樣就會中服務(wù)器的蜜罐圈套嘲碧。服務(wù)器會把所有隱含字段的真實值(或者與表單提交頁面的默認(rèn)值不同的值)都忽略,而且填寫隱含字段的訪問用戶也可能被網(wǎng)站封殺父阻。

如CSS 屬性設(shè)置display:none 進行隱藏愈涩;電話號碼字段name="phone" 是一個隱含的輸入字段;郵箱地址字段name="email" 是將元素向右移動50 000 像素(應(yīng)該會超出電腦顯示器的邊界)并隱藏滾動條

3)圖片驗證碼與動態(tài)網(wǎng)頁抓取JavaScript

google chrome加矛,右鍵-》審查元素-》network-》清空履婉,點擊加載更多--》出現(xiàn)對應(yīng)的get鏈接,尋找type=text/html斟览,點擊查看get參數(shù)或者request url谐鼎。

4)去重與自然語言處理NLTK

5)監(jiān)控網(wǎng)站隨時更新情況,并發(fā)下載趣惠。估算網(wǎng)站大小方法狸棍,是在谷歌搜索site:xxx.com看搜索結(jié)果統(tǒng)計多少條。


?
避免爬蟲陷阱的方案:

1)查看robots文件? ? 2)兩次下載之間添加延時??? 3)圖像識別:Pillow+Tesseract+NumPy? 4)cookie與代理服務(wù)器味悄,避免IP 地址被封殺? ? 5)Selenium 單元測試 等?

查看robots文件:用python的robotparser模塊解析robots文件草戈,以避免下載禁止爬取的URLcan_fetch(user_agent,url),如果結(jié)果值為false侍瑟,則是目標(biāo)網(wǎng)站robots.txt禁止用戶代理為user_agent變量值的爬蟲爬取網(wǎng)站.

兩次下載之間添加延時:設(shè)置延時的間隔值唐片,下次下載時判斷上次訪問時間self.domains.get(urlparse(url).netloc)與當(dāng)前時間的時間差sleepsecs是否在間隔時間內(nèi), 如果是涨颜,則線程睡眠time.sleep(sleepsecs)

其他的部分還沒學(xué)到费韭,先把網(wǎng)絡(luò)抓取部分先把它熟透了吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庭瑰,一起剝皮案震驚了整個濱河市星持,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弹灭,老刑警劉巖督暂,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揪垄,死亡現(xiàn)場離奇詭異,居然都是意外死亡逻翁,警方通過查閱死者的電腦和手機饥努,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來八回,“玉大人酷愧,你說我怎么就攤上這事〔纾” “怎么了伟墙?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滴铅。 經(jīng)常有香客問我戳葵,道長,這世上最難降的妖魔是什么汉匙? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任拱烁,我火速辦了婚禮,結(jié)果婚禮上噩翠,老公的妹妹穿的比我還像新娘戏自。我一直安慰自己,他們只是感情好伤锚,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布擅笔。 她就那樣靜靜地躺著,像睡著了一般屯援。 火紅的嫁衣襯著肌膚如雪猛们。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天狞洋,我揣著相機與錄音弯淘,去河邊找鬼药薯。 笑死楣号,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的怯伊。 我是一名探鬼主播借嗽,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼态鳖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了恶导?” 一聲冷哼從身側(cè)響起浆竭,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后兆蕉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羽戒,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡缤沦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年虎韵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缸废。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡包蓝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出企量,到底是詐尸還是另有隱情测萎,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布届巩,位于F島的核電站硅瞧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏恕汇。R本人自食惡果不足惜腕唧,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘾英。 院中可真熱鬧枣接,春花似錦、人聲如沸缺谴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽湿蛔。三九已至膀曾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阳啥,已是汗流浹背妓肢。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留苫纤,地道東北人碉钠。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像卷拘,于是被迫代替她去往敵國和親喊废。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理栗弟,服務(wù)發(fā)現(xiàn)污筷,斷路器,智...
    卡卡羅2017閱讀 134,693評論 18 139
  • Python爬蟲入門(urllib+Beautifulsoup) 本文包括:1、爬蟲簡單介紹2瓣蛀、爬蟲架構(gòu)三大模塊3...
    廖少少閱讀 9,845評論 0 6
  • Python學(xué)習(xí)網(wǎng)絡(luò)爬蟲主要分3個大的版塊:抓取陆蟆,分析,存儲 另外惋增,比較常用的爬蟲框架Scrapy叠殷,這里最后也詳細(xì)...
    楚江數(shù)據(jù)閱讀 1,469評論 0 6
  • 1 前言 作為一名合格的數(shù)據(jù)分析師,其完整的技術(shù)知識體系必須貫穿數(shù)據(jù)獲取诈皿、數(shù)據(jù)存儲林束、數(shù)據(jù)提取、數(shù)據(jù)分析稽亏、數(shù)據(jù)挖掘壶冒、...
    whenif閱讀 18,078評論 45 523
  • scrapy學(xué)習(xí)筆記(有示例版) 我的博客 scrapy學(xué)習(xí)筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲模...
    陳思煜閱讀 12,709評論 4 46