爬蟲的分類
按使用場景:
- 通用爬蟲:指搜索引擎的爬蟲
- 聚焦爬蟲:指針對特定網站的爬蟲
- 聚焦爬蟲又可以分為大致3種:
-
累積式爬蟲: 從開始到結束,一直不斷爬取,過程中會進行去重操作;
-
增量式爬蟲: 對已經下載的網頁采取增量式更新和只爬行新產生的或者已經發生變化網頁的爬蟲;
-
深度爬蟲: 不能通過靜態鏈接獲取的、隱藏在搜索表單后的,只有用戶提交一些關鍵詞才能獲得的Web面;
requests模塊的使用
說明:Requests是一個用於網絡請求的第三方模塊,繼承了urllib的所有特性,但是其API比urllib容易使用,更符合操作習慣;
GET請求:
import requests
response = requests.get("http://www.baidu.com/")
# 或者
response = requests.request("get", "http://www.baidu.com/")
參數:
- headers:添加請求頭,字典格式,默認的請求頭為request庫,容易被服務器反爬;
- params:添加get參數;
- cookies:添加cookies;
- timeout:設置超時,默認180s,超時后報錯;
import requests
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
params = {'word':"huo"}
response = requests.get("https://www.so.com/", headers=headers,params =params,timeout=5)
POST請求:
import requests
response = requests.post("http://www.baidu.com/",data=data)
# 或者
response = requests.request("post", "http://www.baidu.com/",data=data)
參數:
- data:post請求一般需要提交數據,字典格式;
- 其他參數和get請求一樣;
查看響應的內容:
response.url # 查看url地址
response.status_code # 查看響應狀態碼
response.encoding # 查看相應頭的字符編碼
response.headers # 查看相應頭部
response.text # 自動解碼,返回unicode的自動解碼的內容,有時會失敗產生亂碼
respones.content # 返回二進制的內容
respones.json() # 如果響應是json數據,可以使用
respones.cookies # 獲取cookie的信息,得到的是一個對象,需要裝換格式
- 注意:如果需要接收圖片數據或不知道響應的編碼格式,一般使用respones.content.decode("utf-8"),可以指定解碼的格式;
常見的請求頭的內容
- Host (主機和端口號)
- Connection (鏈接類型)
- Upgrade-Insecure-Requests (升級為HTTPS請求)
- User-Agent (瀏覽器名稱)
- Accept (傳輸文件類型)
- Referer (頁面跳轉處)
- Accept-Encoding(文件編解碼格式)
- Cookie (Cookie)
設置代理
-
設置代理使用參數proxies;
-
免費代理:不需要用戶和密碼,公開的,可去網上尋找
import requests
# 使用參數proxies
proxies = {
"http": "http://host:port", # http的訪問方式
"https": "http://host:port", # https的訪問方式
}
response = requests.get("http://www.baidu.com", proxies = proxies)
- 私密代理:需要用戶和密碼,需要購買
import requests
proxy = { "http": "用戶名:密碼@host:port" }
response = requests.get(url, proxies = proxy)
使用cookies和session
- session:
session表示一個會話,登錄一次后會自動記錄cookie,驗證一次后就可以繞過驗證獲取數據,用來獲取那些需要登陸后才能獲取的網頁數據;
import requests
# 創建一個session會話
s = requests.session()
headers = {"User-Agent": "xxxxx"}
# 表單數據
data = {"user":"xxx", "password":"xxxxx"}
s.post(url, data = data) # 提交 表單,相當於登陸一個網站,后面就可以直接訪問需要登錄才能訪問的url了
- cookies:
cookie是保存在本地的服務器發送的信息,常常可以使用cookie跳過登陸驗證的環節;
import requests
from requests.utils import dict_from_cookiejar
cook = response.cookies
cookdict = dict_from_cookiejar(respones.cookies) # 將獲取的內容轉化成字典的格式
從response中提取數據
-
使用post或get方法提交請求后,會返回response對象,其可能是一個json對象,也可能是html對象,下面討論從html中提取數據。
-
使用正則表達式匹配:不常用
-
csspath:常用,和css選取節點的方法一樣,這里不多做介紹;
-
xpath:(XML language path),常用;
# 需要用到lxml第三方庫
from lxml import etree
import requests
response = requests.get("http://www.baidu.com/")
# 將返回的數據轉化成一個html對象
html = etree.HTML(response.content.decode())
# 使用xpath提取數據
result = html.xpath('//*')
print(result)
- xpath提取數據詳細語法規則:http://www.w3school.com.cn/xpath/index.asp
方法:
- 如果是將響應數據轉化為html對象,使用html = etree.HTML(response));
- 如果是讀取一個html格式的文件,使用html = etree.parse('xxx.html'))
保存數據
- 保存為txt文檔:一般的小文件使用
def save_text(data):
with open("xxx.txt","w") as f:
f.write(data)
- 保存為json文件:在ajax加載上使用較多
import json
def save_json(data):
with open("xxx.json","w") as f:
# 使用dump將python的數據轉換為json格式的數據
# 參數 ensure_ascii=False 禁用ascii編碼,按utf-8編碼
data = json.dump(data, ensure_ascii=False)
f.write(data)
更多json模塊的方法,參考我的另一篇博文:python的json模塊。
- 使用redis、mongodb,mysql等數據庫保存數據。
注意:
- 很多時候需要知道網頁的編碼格式,如是utf-8或gbk等,編碼和解碼的格式必須一樣,不然會產生亂碼,這時可以用chardet模塊查看編碼。
import requests
import chardet
def req(url):
# 設置請求頭
header ={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
}
response = requests.get(url, headers=header, timeout=5)
print(chardet.detect(response.content)) # 得到編碼
return response
if __name__ == "__main__":
response = req("http://www.baidu.com")
print(response.content.decode())
# 結果:{'language': '', 'confidence': 0.99, 'encoding': 'utf-8'}
說明:detect方法返回一個字典,confidence表示判斷的精度,即概率;encoding表示編碼格式。
爬蟲實例
說明:寫一個完整的爬蟲至少包括以下幾個部分:
-
構建請求相關的參數,如headers,cookies,referer,proxes(代理)等,最重要的是初始的url;
-
請求數據,一般使用get,post或head(類似get方法,不過只獲取頭部信息);
-
解析數據:使用正則、jsonpath或xpath提取想要的數據和新的url
-
保存數據:將提取的數據保存到文件或數據庫。
- 一個單線程基礎爬蟲:
實現功能:爬百度貼吧(愛情吧),爬取內容:標題,回復人數,作者;
- 隨機請求頭;
- 有代理池;
- 可以爬取多頁內容;
- url去重;
import requests as res
import sys
from queue import Queue
import random
from lxml import etree
import json
class Baidu_Tieba(object):
def __init__(self, pn):
# 構建請求頭池
self.headers = [
{"User-Agent": "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)"},
{"User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)"},
{"User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0; by TSG)"},
{"User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0; .NET CLR 1.0.3705)"},
]
# 構建代理ip池
self.proxies = [
{'http': 'http://120.236.142.103:8888'},
{'http': 'http://203.195.232.60:8000'},
{'http': 'http://202.38.254.247:3128'},
{'http': 'http://116.199.2.209:80'},
{'http': 'http://111.62.251.130:8081'},
{'http': 'http:116.199.115.78:80'},
{'http': 'http://120.236.142.103:8888'}
]
# 設置爬取多少頁
self.pn = pn
# 設置初始url
self.base_url = 'https://tieba.baidu.com/f?ie=utf-8&kw=愛情吧&fr=search'
# 用來去重的集合
self.url_set = set()
# url請求隊列,無所謂深度和廣度優先,使用先進先出隊列
self.url_list = Queue()
# 請求數據
def get_page(self, url):
header = random.choice(self.headers)
proxies = random.choice(self.proxies)
response = res.get(url, headers=header, proxies=proxies)
return response.content
# 解析數據
def analysis_data(self, data):
html = etree.HTML(data.decode())
# 提取頁面內容
nodes = html.xpath('//*[@id="thread_list"]/li/div')
data_list = []
# 獲取內容
for node in nodes:
item = {}
item["標題"] = node.xpath('./div[2]/div[1]/div[1]/a/text()')
item["評論"] = node.xpath('./div[1]/span/text()|./div[1]/div/text()')
item['用戶'] = node.xpath('./div[2]/div[1]/div[2]/span[1]/span[1]/a/text()')
data_list.append(item)
print(item)
# 獲取url
url_nodes = html.xpath('//*[@id="frs_list_pager"]/a/@href')
print(url_nodes)
for url in url_nodes:
url = 'https:' + url
self.url_list.put(url)
return data_list
# 保存數據
def save_data(self, data):
filename = '愛情吧.json'
with open(filename, "w", encoding='utf-8') as f:
for d in data:
da = json.dumps(d)
f.write(da+',\n')
def run(self):
# 將初始url加入到url隊列
self.url_list.put(self.base_url)
i = 0
while i < self.pn:
# 取出一條url
url = self.url_list.get()
# print(url)
# 驗證
if url not in self.url_set:
i += 1
print(i)
self.url_set.add(url)
data = self.get_page(url)
data_list = self.analysis_data(data)
self.save_data(data_list)
if __name__ == "__main__":
if len(sys.argv) < 3:
print("參數不足,如'python 百度貼吧爬蟲.py 3'")
else:
np = sys.argv[1]
tieba = Baidu_Tieba(int(np))
tieba.run()
- 注意
百度貼吧的html對以Chrome為內核的瀏覽器發送的是被注釋的源碼,所以xpath得不到相關的內容,必須設置User-Agent是ie內核的。
- 說明:
上述爬蟲是最基礎的單線程爬蟲,其效率和抗風險的能力是很差的,如:
- 當代理服務器失效,或無法訪問的時候,會阻塞爬蟲的運行或超時錯誤;
- 當百度貼吧的服務器沒有響應的時候,程序阻塞;
- 當爬取的頁面過多時,可能會陷入死循環;
為了提高爬蟲的效率和抗風險的能力,需要進一步學習爬蟲知識,下一篇博文將繼續介紹。
- 作者:天宇之游
- 出處:http://www.cnblogs.com/cwp-bg/
- 本文版權歸作者和博客園共有,歡迎轉載、交流,但未經作者同意必須保留此段聲明,且在文章明顯位置給出原文鏈接。