再来一波流浪地球的爬虫,不过这次爬的是猫眼电影。而且这次不再是用scrapy而是用最常规的方法requests
。
同时这里还要详细讲几个别的问题。
- 什么是Ajax
- 如何进行反爬
- 如何储存到MongoDB中
首先,常规操作进行猫眼电影的网址进行分析。但是我们发现在其网页端,它的评论往往没有那么多。
此时我们用的是猫眼电影的手机端url:http://m.maoyan.com/movie/248906/morecomments?_v_=yes
大家可以往下滑,发现在页面没有跳转的情况下,内容就加载出来了,可以知道这里用的是Ajax加载的方式。
那么什么是Ajax呢?
Ajax
Ajax,全称为Asynchronous JavaScript and XML,即异步的JavaScript和XML。它不是一门编程语言,而是利用JavaScript在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页的技术。
对于传统的网页,如果想更新其内容,那么必须要刷新整个页面,但有了Ajax,便可以在页面不被全部刷新的情况下更新其内容。在这个过程中,页面实际上是在后台与服务器进行了数据交互,获取到数据之后,再利用JavaScript改变网页,这样网页内容就会更新了。
Ajax实现原理
Ajax的原理简单来说通过XmlHttpRequest
对象来向服务器发异步请求,从服务器获得数据,然后用javascript
来操作DOM
而更新页面。
具体大家可以自行Google哦。
Ajax分析方法
那么我们在刷新,获取信息的时候,我们看到的url是没有变化的,而是一次次的Ajax请求渲染了网页。那么我们应该去哪里查看呢?
当然是右键
——检查
,借助开发者工具进行检查。
不过这一次我们关注的就不是Elements
选项卡了,而是Network
选项卡。
我们进行切换并且刷新,就能获得如图所示的信息:
那么在这里:
选择XHR
,筛选出来的就是Ajax请求啦。
我们选择开头有comments.json
的条目,在它的Request Headers
可以看到比传统的请求头中多了一项X-Requested-With: superagent
,而superagent
是一种轻量级的Ajax请求方式。
随后,我们点击Preview
选项卡,可以看到网页是json
格式,而chrome已经帮我们解析出来了
可以看到在data
下,有我们需要的东西。
此时,想要获取这些内容非常容易了,就像是获取字典的值一样。
res = requests.get(url,timeout,headers=self.headers)
comments = res.json()['data']['comments']
这样我们就能获取评论数据,进行下一步提取了。
同时我们在Headers
里可以看到真实的url:http://m.maoyan.com/review/v2/comments.json?movieId=248906&userId=-1&offset=0&limit=15&ts=0&level=2&type=3
,并且请求方式是get
。
那么我们多点击几个,观察url就会发现,其中变化的只有参数offset
,并且是每15进行累加。
代码
直接上代码吧。
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('网页获取出错,10s后将获取倒数第{}次'.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("//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()函数里写了一大段,其实是为了更好的反爬。
首先就是请求头,写上了heasers
、user-agent
和cookie
,这是为了让网站认为这是一个人发送的请求,而不是机器。
其次,这里多加了一个代理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("//table[@class='fl-table']/tbody/tr[{}]/td[1]/text()".format(random.randint(1,50)))
proxy = {
'https':str(result[0])
}
return proxy
代理是通过一个免费网站中获得的,随机取得一个ip,用于requests
虽然代码很长,但是简单,就是有点繁琐。
同时在请求一个url后,经常会time.sleep()
,否则,请求的太过于频繁,就会被封的。
最后requests.get
中的timeout
参数可以设定等待连接的秒数,超过时间,就会抛出异常。
储存
这一次,我们将数据存入MongoDB
中
准备工作
具体自行搜索啦,怎么安装,下载。
连接MongoDB
连接MongoDB时,我们需要使用的是,PyMongo库里面的MongoClient
。一般来说,传入MongoDB的ip和端口即可。
import pymongo import MongoClient
client = pymongo.MongoClient(host='localhost',port=27017)
这样就可以创建MoongoDB的连接对象啦。
当然我们也可以换种方式:
client = MongoClient('mongodb://localhost:27017/')
指定数据库
MongoDB中可以建立多个数据库,接下来我们需要指定操作哪个数据库,在这里,我们以建立maoyan
为例:
db = client['maoyan']
或者
db = client.maoyan
指定集合
在每个数据库里又有许多集合(collection),它们类似于关系型数据库中的表。
下一步就是要指定操作的集合,同样我们建立名为lldq
的集合:
collection = db['lldq']
或者
collection = db.lldq
插入数据
此时,我们可以插入数据了。
一般数据以字典的形式表示,比如我们如果要获取我们想要的id,性别,评论,分数,等等:
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()
,就要将数据以列表的形式传递。
比如,你有两个字典,分别为dic1,dic2,要存储到mongodb
collection.insert_many([dic1,dic2])
可视化工具
最后储存了一大堆,可是怎么看呢?
这里推荐Robo 3T
,官方网站,三大平台都支持,下载链接。
另外,还有一个简单易用的可视化工具——Studio 3T,它同样具有方便的图形化管理界面,官方网站,同样支持三大平台,下载链接。
预告
接下俩,会讲讲,可视化函数,以及对这次爬取到的数据的处理。
最后请大家批评指正!