再來一波流浪地球的爬蟲莉兰,不過這次爬的是貓眼電影崔挖。而且這次不再是用scrapy而是用最常規(guī)的方法requests
矢渊。
同時(shí)這里還要詳細(xì)講幾個(gè)別的問題战转。
- 什么是Ajax
- 如何進(jìn)行反爬
- 如何儲(chǔ)存到MongoDB中
首先,常規(guī)操作進(jìn)行貓眼電影的網(wǎng)址進(jìn)行分析予弧。但是我們發(fā)現(xiàn)在其網(wǎng)頁端刮吧,它的評(píng)論往往沒有那么多。
此時(shí)我們用的是貓眼電影的手機(jī)端url:http://m.maoyan.com/movie/248906/morecomments?_v_=yes
大家可以往下滑掖蛤,發(fā)現(xiàn)在頁面沒有跳轉(zhuǎn)的情況下杀捻,內(nèi)容就加載出來了,可以知道這里用的是Ajax加載的方式蚓庭。
那么什么是Ajax呢致讥?
Ajax
Ajax,全稱為Asynchronous JavaScript and XML彪置,即異步的JavaScript和XML拄踪。它不是一門編程語言蝇恶,而是利用JavaScript在保證頁面不被刷新拳魁、頁面鏈接不改變的情況下與服務(wù)器交換數(shù)據(jù)并更新部分網(wǎng)頁的技術(shù)。
對(duì)于傳統(tǒng)的網(wǎng)頁撮弧,如果想更新其內(nèi)容潘懊,那么必須要刷新整個(gè)頁面姚糊,但有了Ajax,便可以在頁面不被全部刷新的情況下更新其內(nèi)容授舟。在這個(gè)過程中救恨,頁面實(shí)際上是在后臺(tái)與服務(wù)器進(jìn)行了數(shù)據(jù)交互,獲取到數(shù)據(jù)之后释树,再利用JavaScript改變網(wǎng)頁肠槽,這樣網(wǎng)頁內(nèi)容就會(huì)更新了。
Ajax實(shí)現(xiàn)原理
Ajax的原理簡單來說通過XmlHttpRequest
對(duì)象來向服務(wù)器發(fā)異步請(qǐng)求奢啥,從服務(wù)器獲得數(shù)據(jù)秸仙,然后用javascript
來操作DOM
而更新頁面。
具體大家可以自行Google哦桩盲。
Ajax分析方法
那么我們?cè)谒⑿录偶停@取信息的時(shí)候,我們看到的url是沒有變化的赌结,而是一次次的Ajax請(qǐng)求渲染了網(wǎng)頁捞蛋。那么我們應(yīng)該去哪里查看呢?
當(dāng)然是右鍵
——檢查
柬姚,借助開發(fā)者工具進(jìn)行檢查拟杉。
不過這一次我們關(guān)注的就不是Elements
選項(xiàng)卡了,而是Network
選項(xiàng)卡量承。
我們進(jìn)行切換并且刷新捣域,就能獲得如圖所示的信息:
那么在這里:
選擇XHR
,篩選出來的就是Ajax請(qǐng)求啦宴合。
我們選擇開頭有comments.json
的條目焕梅,在它的Request Headers
可以看到比傳統(tǒng)的請(qǐng)求頭中多了一項(xiàng)X-Requested-With: superagent
,而superagent
是一種輕量級(jí)的Ajax請(qǐng)求方式卦洽。
隨后贞言,我們點(diǎn)擊Preview
選項(xiàng)卡,可以看到網(wǎng)頁是json
格式阀蒂,而chrome已經(jīng)幫我們解析出來了
可以看到在data
下该窗,有我們需要的東西。
此時(shí)蚤霞,想要獲取這些內(nèi)容非常容易了酗失,就像是獲取字典的值一樣。
res = requests.get(url,timeout,headers=self.headers)
comments = res.json()['data']['comments']
這樣我們就能獲取評(píng)論數(shù)據(jù)昧绣,進(jìn)行下一步提取了规肴。
同時(shí)我們?cè)?code>Headers里可以看到真實(shí)的url:http://m.maoyan.com/review/v2/comments.json?movieId=248906&userId=-1&offset=0&limit=15&ts=0&level=2&type=3
,并且請(qǐng)求方式是get
。
那么我們多點(diǎn)擊幾個(gè)拖刃,觀察url就會(huì)發(fā)現(xiàn)删壮,其中變化的只有參數(shù)offset
,并且是每15進(jìn)行累加兑牡。
代碼
直接上代碼吧央碟。
import requests
import time
from datetime import datetime
from pymongo import MongoClient
from lxml import etree
import random
class maoyan():
def __init__(self):
self.headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
'cookie':"_lxsdk_cuid=163f4936fa628-0651e9953b7cba-791238-144000-163f4936fa7c8; v=3; iuuid=A83BE960AA7E11E8B6349B995F5768EC558368427A114227869310FFD50B50A1; webp=true; ci=185%2C%E5%98%89%E5%85%B4; _lx_utm=utm_source%3Dgoogle%26utm_medium%3Dorganic; _lxsdk=438345E032A511E9BDC5F9F05A760829F59E3BE1237E422D9B95E24FCA63731A; __mta=254523319.1528816694943.1535432227454.1550402539437.7; _lxsdk_s=168fb2aa1e2-1c1-2d8-2a8%7C%7C24",
}
#反爬
def get_comments(self,i,timeout,proxy=None):
page = i*15
num_retries = 6
url = 'http://m.maoyan.com/review/v2/comments.json?movieId=248906&userId=-1&offset={}&lim it=15&ts=1550476464599&level=2&type=3'.format(page)
if proxy == None:
try:
res = requests.get(url,timeout,headers=self.headers)
except:
if num_retries>0:
time.sleep(10)
print('網(wǎng)頁獲取出錯(cuò),10s后將獲取倒數(shù)第{}次'.format(num_retries))
res= requests.get(url,timeout,headers=self.headers)
num_retries -= 1
else:
print('開始使用代理')
time.sleep(10)
proxy = self.get_ip
res = requests.get(url,timeout,proxies=proxy,headers=self.headers)
else:
try:
proxy = self.get_ip
res = requests.get(url,timeout,proxies=proxy,headers=self.headers)
except:
if num_retries > 0:
time.sleep(10)
proxy = self.get_ip
res = requests.get(url,timeout,proxies=proxy,headers=self.headers)
else:
print('代理失敗均函,取消代理')
res = requests.get(url,timeout,headers=self.headers)
comments = res.json()['data']['comments']
return comments
#保存到MongoDB中
def save_data(self,comment):
client = MongoClient()
db = client['maoyan']
collection = db['lldq']
try:
if collection.insert_one(comment):
print('Saved to Mongo')
except:
print('無法保存')
#獲取代理
def get_ip(self):
url = 'http://www.xiladaili.com/http/'
response = requests.get(url)
t = response.text
html = etree.HTML(t)
result = html.xpath("http://table[@class='fl-table']/tbody/tr[{}]/td[1]/text()".format(random.randint(1,50)))
proxy = {
'https':str(result[0])
}
return proxy
if __name__ == '__main__':
for i in range(0,500):
if i%5 == 0:
time.sleep(1)
my = maoyan()
d = my.get_comments(i,timeout=3)
my.parse_comments(d,i)
反爬
我在get_comments()函數(shù)里寫了一大段亿虽,其實(shí)是為了更好的反爬。
首先就是請(qǐng)求頭苞也,寫上了heasers
经柴、user-agent
和cookie
,這是為了讓網(wǎng)站認(rèn)為這是一個(gè)人發(fā)送的請(qǐng)求墩朦,而不是機(jī)器坯认。
其次,這里多加了一個(gè)代理proxies
:
def get_ip(self):
url = 'http://www.xiladaili.com/http/'
response = requests.get(url)
t = response.text
html = etree.HTML(t)
result = html.xpath("http://table[@class='fl-table']/tbody/tr[{}]/td[1]/text()".format(random.randint(1,50)))
proxy = {
'https':str(result[0])
}
return proxy
代理是通過一個(gè)免費(fèi)網(wǎng)站中獲得的氓涣,隨機(jī)取得一個(gè)ip牛哺,用于requests
雖然代碼很長,但是簡單劳吠,就是有點(diǎn)繁瑣引润。
同時(shí)在請(qǐng)求一個(gè)url后,經(jīng)常會(huì)time.sleep()
痒玩,否則淳附,請(qǐng)求的太過于頻繁,就會(huì)被封的蠢古。
最后requests.get
中的timeout
參數(shù)可以設(shè)定等待連接的秒數(shù)奴曙,超過時(shí)間,就會(huì)拋出異常草讶。
儲(chǔ)存
這一次洽糟,我們將數(shù)據(jù)存入MongoDB
中
準(zhǔn)備工作
具體自行搜索啦,怎么安裝堕战,下載坤溃。
連接MongoDB
連接MongoDB時(shí),我們需要使用的是嘱丢,PyMongo庫里面的MongoClient
薪介。一般來說,傳入MongoDB的ip和端口即可越驻。
import pymongo import MongoClient
client = pymongo.MongoClient(host='localhost',port=27017)
這樣就可以創(chuàng)建MoongoDB的連接對(duì)象啦汁政。
當(dāng)然我們也可以換種方式:
client = MongoClient('mongodb://localhost:27017/')
指定數(shù)據(jù)庫
MongoDB中可以建立多個(gè)數(shù)據(jù)庫道偷,接下來我們需要指定操作哪個(gè)數(shù)據(jù)庫,在這里烂完,我們以建立maoyan
為例:
db = client['maoyan']
或者
db = client.maoyan
指定集合
在每個(gè)數(shù)據(jù)庫里又有許多集合(collection),它們類似于關(guān)系型數(shù)據(jù)庫中的表。
下一步就是要指定操作的集合诵棵,同樣我們建立名為lldq
的集合:
collection = db['lldq']
或者
collection = db.lldq
插入數(shù)據(jù)
此時(shí)抠蚣,我們可以插入數(shù)據(jù)了。
一般數(shù)據(jù)以字典的形式表示履澳,比如我們?nèi)绻@取我們想要的id嘶窄,性別,評(píng)論距贷,分?jǐn)?shù)柄冲,等等:
comment = {'content': com['content'], 'gender': com['gender'],
'id': com['id'],
'nick': com['nick'],
'replyCount': com['replyCount'],
'score': com['score'],
'time': com['time'],
'upCount': com['upCount'],
'userId': com['userId'],
'userLevel': com['userLevel']
}
諸如這種形式。
我們可以使用insert_one()
和insert_many()
方法來插入單條和多條記錄
collection.insert_one(comment)
如果你是用的是insert_many()
忠蝗,就要將數(shù)據(jù)以列表的形式傳遞现横。
比如,你有兩個(gè)字典阁最,分別為dic1,dic2戒祠,要存儲(chǔ)到mongodb
collection.insert_many([dic1,dic2])
可視化工具
最后儲(chǔ)存了一大堆,可是怎么看呢速种?
這里推薦Robo 3T
,官方網(wǎng)站姜盈,三大平臺(tái)都支持,下載鏈接配阵。
另外馏颂,還有一個(gè)簡單易用的可視化工具——Studio 3T,它同樣具有方便的圖形化管理界面棋傍,官方網(wǎng)站救拉,同樣支持三大平臺(tái),下載鏈接瘫拣。
預(yù)告
接下倆近上,會(huì)講講,可視化函數(shù)拂铡,以及對(duì)這次爬取到的數(shù)據(jù)的處理壹无。
最后請(qǐng)大家批評(píng)指正!