用
python
寫爬蟲爬取須要的數據比較容易,以Python
簡潔的語法和一大波成熟的庫,寫起來至關的快html
python的版本以及使用的庫python
此次主要是爬取貓眼的熱映電影,網址爲http://maoyan.com/films
瀏覽器
我用 類(Class) 的方式建立,並初始化須要使用的參數bash
class MaoYanHot:
def __init__(self):
self.session = requests.session() # requests庫的session對象可以幫咱們跨請求保持某些參數,也會在同一個session實例發出的全部請求之間保持cookies
self.baseUrl = 'http://maoyan.com/films'
# 設置requests請求的headers參數來模擬瀏覽器訪問
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
'Accept': 'text/html;q=0.9,*/*;q=0.8',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'Accept-Encoding': 'gzip',
'Connection': 'close',
'Referer': None # 注意若是依然不能抓取的話,這裏能夠設置抓取網站的host
}
# 開始訪問
def run(self, pages='?offset=0'):
url = self.baseUrl + pages
print('獲取url:' + url)
MaoYanHot = MaoYanHot()
MaoYanHot.run()
複製代碼
寫個def
來獲取網頁內容,並在run
裏面調用cookie
# 獲取網頁內容
def getHtmlContent(self, url):
try:
respone = self.session.get(url, headers=self.headers)
if respone.status_code == 200:
# 這裏的編碼取決於網頁編碼
# 在decode的時候指定錯誤處理方式,有strict, ignore和replace三種處理方式,strict是默認的也就是拋出異常,ignore是忽略非法字符
return respone.content.decode('utf-8', 'ignore')
return False
except Exception as e:
print('err1:' + str(e))
return False
複製代碼
在run裏面調用contents = self.getHtmlContent(url)
session
若是順利的話就會獲得HTML內容,經過HTML發現dd標籤並無閉合須要處理下 dom
etree.HTML
我發現並不能正確閉合dd標籤,形成層層嵌套,寫個例子測試下
from lxml import etree
aaa = ''' <dl> <dd>1111 <dd>1111 <dd>1111 <dd>1111 </dl> '''
bbb = etree.HTML(aaa)
print(etree.tostring(bbb))
複製代碼
打印的結果是b'<html><body><dl>\n <dd>1111\n <dd>1111\n <dd>1111\n <dd>1111\n</dd></dd></dd></dd></dl>\n</body></html>'
測試
手動閉合dd
後經過etree.HTML
把HTML轉爲XML,利用xpath語法
能夠快速匹配咱們須要的節點。網站
# 把html轉爲xml
def htmlToXml(self, contents):
# 手動閉合dd標籤
contents = contents.replace('<dd', '</dd><dd')
return etree.HTML(contents)
複製代碼
在run裏面調用xmls = self.htmlToXml(contents)
再來看看電影結構組成,以下圖 ui
dd
裏面,評分分爲
暫無評分 和
具體評分 2種,父元素是
dl
,而它的
class[movie-list]
在頁面只有一個。
接下來寫個def
分析須要的電影數據,如電影的名稱和評分
# 利用xpath解析html內容
def parsingHtml(self, html):
try:
# 選擇文檔中的節點,而不考慮它們的位置。選取屬性class="movie-list"的 dl 節點,這裏返回的是dd列表集合
movieList = html.xpath('//dl[@class="movie-list"]/dd')
if len(movieList):
for item in movieList:
# 從當前的 dd 節點選取屬性data-act="movies-click"的 a 的節點的文本內容,這裏匹配的是電影名稱
movieName = item.xpath('.//a[@data-act="movies-click"]/text()')[0]
# 從當前的 dd 節點選取屬性class="channel-detail channel-detail-orange的 div 的節點的文本內容,這裏匹配的是暫無評分
moviePa = item.xpath('.//div[@class="channel-detail channel-detail-orange"]/text()')
# moviePa沒有值說明是有具體評分
if not moviePa:
# 匹配評分的整數
integer = item.xpath('.//div[@class="channel-detail channel-detail-orange"]/i[@class="integer"]/text()')
# 匹配評分的小數
fraction = item.xpath('./div[@class="channel-detail channel-detail-orange"]/i[@class="fraction"]/text()')
moviePa = integer + fraction
yield({
'movieName': movieName,
'moviePa': ''.join(moviePa)
})
return
except Exception as e:
print('err2:' + str(e))
return
複製代碼
在run裏面調用movieList = self.parsingHtml(xmls)
,經過for
循環打印電影數據
for item in movieList:
strs = '電影:%s -- 評分: %s' % (item['movieName'], item['moviePa'])
print(strs)
複製代碼
輸出以下:
獲取url:http://maoyan.com/films?offset=0
電影:復仇者聯盟3:無限戰爭 -- 評分: 8.6
電影:我是你媽 -- 評分: 8.2
電影:後來的咱們 -- 評分: 8.1
電影:超時空同居 -- 評分: 8.8
電影:寂靜之地 -- 評分: 暫無評分
電影:幕後玩家 -- 評分: 8.1
電影:狂暴巨獸 -- 評分: 9.0
電影:戰犬瑞克斯 -- 評分: 8.2
電影:巴霍巴利王2:終結 -- 評分: 8.7
電影:親愛的,我要和別人結婚了 -- 評分: 8.2
電影:小公主艾薇拉與神祕王國 -- 評分: 5.7
電影:晝顏 -- 評分: 暫無評分
電影:頭號玩家 -- 評分: 9.1
電影:七號公館 -- 評分: 暫無評分
電影:冰雪女王3:火與冰 -- 評分: 8.2
電影:侏羅紀世界2 -- 評分: 暫無評分
電影:瑪麗與魔女之花 -- 評分: 8.0
電影:復仇者聯盟4 -- 評分: 暫無評分
電影:青年馬克思 -- 評分: 8.4
電影:狄仁傑之四大天王 -- 評分: 暫無評分
電影:愛情公寓 -- 評分: 暫無評分
電影:路過將來 -- 評分: 暫無評分
電影:21克拉 -- 評分: 8.2
電影:遊俠索羅:星球大戰外傳 -- 評分: 暫無評分
電影:午夜十二點 -- 評分: 3.5
電影:龍在哪裏? -- 評分: 8.6
電影:火魔高跟鞋 -- 評分: 暫無評分
電影:動物世界 -- 評分: 暫無評分
電影:犬之島 -- 評分: 7.9
電影:厲害了,個人國 -- 評分: 9.6
複製代碼
自此已經能夠請求網頁而且獲取須要的數據,接下來自動請求下一頁。經過分析發現URl都是http://maoyan.com/films
拼接?offset=x
這個參數,x是當前頁數的起始數,第一頁x
爲0,第二頁x
爲30,以此類推。恰好下一頁的href
就是這個參數。
寫個def
獲取下一個的參數
# 匹配下一頁
def getNextPage(self, html):
try:
# 獲取a元素的文本值爲下一頁的href屬性
result = html.xpath('//a[text()="下一頁"]/@href')
return result[0] if len(result) else False
except Exception as e:
print('err3:' + str(e))
return False
複製代碼
在run裏面經過遞歸的方式實現自動獲取下一頁的數據,並隨機休眠5-20秒以預防被貓眼拒絕訪問
NextPage = self.getNextPage(xmls)
if NextPage:
sleepTime = random.randint(5, 20)
print('休眠時間:%s' % (sleepTime))
# time.sleep(sleepTime)
sarr = list(range(sleepTime))
sarr.sort(reverse=True)
for iss in sarr:
time.sleep(1)
print('倒計時:' + str(iss))
self.run(NextPage)
複製代碼
到這裏已經基本完成了一個爬蟲
有些網站內容必須須要登錄了才能訪問,手動設置cookie的方式應該是比較省力的,經過瀏覽器登錄網頁後,經過F12
- Network
- Headers
- Request Headers
找到 Cookie
字段裏面包含了cookie信息,寫個def
把字符串轉爲字典
def dictToCookie(self):
cookie_str = 'uuid=1A6E888B4A4B29B16FBA1299108DBE9C716675A309C3BAA9D55E347DEB6E83EC; _lxsdk_cuid=16367a2e6a9c8-0d58eea69a4d89-3c3c5905-1fa400-16367a2e6a9c8; _lxsdk=1A6E888B4A4B29B16FBA1299108DBE9C716675A309C3BAA9D55E347DEB6E83EC; _csrf=89ce8482c620a863330c9ffcaf2e20579a55a76cbd8637ac62a2b9f72c172a7b; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; lt=DiIgzaThdLC5XZdcq4joMkZRpD8AAAAA3QUAAIpNTDuzpXNwUmtKXTXO6i9RayLLEM7KoYPwtHI1-tbRoMyue5EEa87kRKXrkA9SxQ; lt.sig=HiWqeKJoko8oWV-YaVVbIHt4iXQ; __mta=46043675.1526452119497.1526534209978.1526537087635.59; _lxsdk_s=1636cb36a83-aff-08c-b8a%7C%7C2'
cookie_dict = dict()
for item in cookie_str.split(';'):
key, value = item.split('=', 1) # 1表明只分一次,獲得兩個數據
cookie_dict[key] = value
return cookie_dict
複製代碼
而後在class
的__init__
裏面把字典轉爲cookiejar
並設置cookie
cookie_dict = self.dictToCookie()
self.session.cookies = requests.utils.cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True)
複製代碼
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-05-16 15:14:06
# @Author : Your Name (you@example.org)
# @Link : link
# @Version : 1.0.0
# 獲取貓眼熱門電影
import time
import requests
import random
from lxml import etree
class MaoYanHot:
def __init__(self):
self.session = requests.session()
self.baseUrl = 'http://maoyan.com/films'
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
'Accept': 'text/html;q=0.9,*/*;q=0.8',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'Accept-Encoding': 'gzip',
'Connection': 'close',
'Referer': None # 注意若是依然不能抓取的話,這裏能夠設置抓取網站的host
}
cookie_dict = self.dictToCookie()
self.session.cookies = requests.utils.cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True)
# 轉爲字典
def dictToCookie(self):
cookie_str = 'uuid=1A6E888B4A4B29B16FBA1299108DBE9C716675A309C3BAA9D55E347DEB6E83EC; _lxsdk_cuid=16367a2e6a9c8-0d58eea69a4d89-3c3c5905-1fa400-16367a2e6a9c8; _lxsdk=1A6E888B4A4B29B16FBA1299108DBE9C716675A309C3BAA9D55E347DEB6E83EC; _csrf=89ce8482c620a863330c9ffcaf2e20579a55a76cbd8637ac62a2b9f72c172a7b; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; lt=DiIgzaThdLC5XZdcq4joMkZRpD8AAAAA3QUAAIpNTDuzpXNwUmtKXTXO6i9RayLLEM7KoYPwtHI1-tbRoMyue5EEa87kRKXrkA9SxQ; lt.sig=HiWqeKJoko8oWV-YaVVbIHt4iXQ; __mta=46043675.1526452119497.1526534209978.1526537087635.59; _lxsdk_s=1636cb36a83-aff-08c-b8a%7C%7C2'
cookie_dict = dict()
for item in cookie_str.split(';'):
key, value = item.split('=', 1) # 1表明只分一次,獲得兩個數據
cookie_dict[key] = value
return cookie_dict
# 獲取網頁內容
def getHtmlContent(self, url):
try:
respone = self.session.get(url, headers=self.headers)
if respone.status_code == 200:
return respone.content.decode('utf-8')
return False
except Exception as e:
print('err1:' + str(e))
return False
# 把html轉爲xml
def htmlToXml(self, contents):
# 手動閉合dd標籤
contents = contents.replace('<dd', '</dd><dd')
return etree.HTML(contents)
# 利用xpath解析html內容
def parsingHtml(self, html):
try:
movieList = html.xpath('//dl[@class="movie-list"]/dd')
if len(movieList):
for item in movieList:
movieName = item.xpath('.//a[@data-act="movies-click"]/text()')[0]
moviePa = item.xpath('.//div[@class="channel-detail channel-detail-orange"]/text()')
if not moviePa:
integer = item.xpath('.//div[@class="channel-detail channel-detail-orange"]/i[@class="integer"]/text()')
fraction = item.xpath('./div[@class="channel-detail channel-detail-orange"]/i[@class="fraction"]/text()')
moviePa = integer + fraction
yield({
'movieName': movieName,
'moviePa': ''.join(moviePa)
})
return
except Exception as e:
print('err2:' + str(e))
return
# 匹配下一頁
def getNextPage(self, html):
try:
result = html.xpath('//a[text()="下一頁"]/@href')
return result[0] if len(result) else False
except Exception as e:
print('err3:' + str(e))
return False
# 開始訪問
def run(self, pages='?offset=18180'):
url = self.baseUrl + pages
print('獲取url:' + url)
contents = self.getHtmlContent(url)
if contents:
xmls = self.htmlToXml(contents)
movieList = self.parsingHtml(xmls)
file_object = open('movie.txt', 'a+', encoding='utf-8')
file_object.write('獲取url:' + url + "\n")
for item in movieList:
strs = '電影:%s -- 評分: %s' % (item['movieName'], item['moviePa'])
print(strs)
file_object.write(strs + "\n")
file_object.close()
NextPage = self.getNextPage(xmls)
if NextPage:
sleepTime = random.randint(5, 20)
print('休眠時間:%s' % (sleepTime))
# time.sleep(sleepTime)
sarr = list(range(sleepTime))
sarr.sort(reverse=True)
for iss in sarr:
time.sleep(1)
print('倒計時:' + str(iss))
self.run(NextPage)
else:
print('網頁內容獲取失敗')
MaoYanHot = MaoYanHot()
MaoYanHot.run()
複製代碼