动态网站抓取
动态网页的抓取比静态网页困难一些,主要涉及到Ajax和动态HTML。简单的网页访问是无法获取完整的数据的,需要对数据加载流程进行分析。
Ajax和动态HTML
传统的WEB应用,提交一个表单请求给服务器,服务器接收到请求之后,返回一个新的页面给浏览器,有点浪费和影响用户体验。
Ajax,异步的JavaScript和XML,是JavaScript异步加载技术,XML以及Dom。使用Ajax技术不必刷新整个页面,只需对页面的局部进行更新。
DHTML,动态的HTML,相对传统的静态HTML而言的一种制作网页的概念。
判断是否动态网站还是静态网站
使用requests访问一个网页,返回的Response内容和在浏览器上看的HTML内容不一样时。
动态时候,无法从响应中抽取出有效的数据。
一种时直接从JavaScript中采集加载的数据,另一种直接采取浏览器已经加载和好的数据。
动态爬虫1:爬取影评信息
例子 MTime电影网(www.mtime.com)进行分析 获取正在上映和未上映的影视信息。
先访问 http://www.mtime.com/ 。F12监听网络
查看所有的请求中,找出响应数据是页面上展示的响应。
编辑网页下载器
import requests
class HtmlDownloader(object):
def download(self, url):
if url is None:
return None
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
host = 'www.mtime.com'
headers = {'User-Agent': user_agent,
'Host': host}
r = requests.get(url, headers=headers)
if r.status_code == 200:
r.encoding = 'utf-8'
return r.text
return None
def infoDownload(self, url):
if url is None:
return None
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
host = 'front-gateway.mtime.com'
headers = {'User-Agent': user_agent,
'Host': host}
r = requests.get(url, headers=headers)
if r.status_code == 200:
r.encoding = 'utf-8'
return r.json()
return None
网页解析器
网页解析器中主要包括两个部分,一个从当前网页中提取相关联的电影链接,另有一个是从加载链接中提取我们所需的字段。
通过提取相关的电影链接,会发现影视内容 没有在页面上展示 在一个
里 需要找出请求响应里包含影视内容的请求
http://front-gateway.mtime.com/library/movie/detail.api?tt=1664074173339&movieId=268954&locationId=290
参数: tt 应该是时间戳 movieId 影视的编号 locationId 不清楚,没有这个属性也能调用
这是只要提取 相关影视的编号就行,然后更改movieId的值就行
http://front-gateway.mtime.com/library/movie/detail.api?tt=1664074173339&movieId=268954&locationId=290
参数: tt 应该是时间戳 movieId 影视的编号 locationId 不清楚,没有这个属性也能调用
这是只要提取 相关影视的编号就行,然后更改movieId的值就行
import re
import json
from urllib import parse
from bs4 import BeautifulSoup
class HtmlParser(object):
def parser_url(self, page_url, response):
"""
提页面中相关的影视
:param page_url:
:param response:
:return:
"""
urls = {}
# 上映 未上映
show_urls = []
future_urls = []
soup = BeautifulSoup(response, 'lxml')
finds = soup.select('.cinemaregion-box > div')
for find in finds:
lis = find.select('.imgrollinner > ul > li')
for li in lis:
a = li.find('a')
a_href = a['href']
pattern = re.compile(r'\d+')
result = re.search(pattern, a_href)
if a['data-pan'].find('futureSchdule') > -1:
future_urls.append(result.group())
else:
show_urls.append(result.group())
urls['show_urls'] = show_urls
urls['future_urls'] = future_urls
if urls is None:
return None
else:
return urls
def parser_json(self, page_url, response):
"""
解析响应
:param page_url:
:param response:
:return:
"""
if response is not None:
# json模块加载字符串
try:
code = response.get('code')
msg = response.get('msg')
if code == 0:
data = response.get('data')
if data:
return self._parser_release(page_url, data)
else:
print('没有获取到影视信息数据')
return None
else:
print('请求失败', code, msg)
return None
except Exception as e:
print(e)
return None
def _parser_release(self, page_url, value):
"""
解析 已经上映的影片
:param page_url: 电影链接
:param value: 数据
:return:
"""
try:
# 基础信息
basic = value.get('basic')
# id 名字 英文名字 链接 状态 类型 评分 演员表 导演 编剧 剧情
movieId = basic.get('movieId')
name = basic.get('name')
nameEn = basic.get('nameEn')
url = basic.get('url')
movieStatus = basic.get('movieStatus')
types = basic.get('type')
overallRating = basic.get('overallRating')
actors = []
for actor in basic.get('actors'):
actors.append(actor.get('name'))
director = ''
if basic.get('director') is not None:
director = basic.get('director').get('name')
writers = []
for writer in basic.get('writers'):
writers.append(writer.get('name'))
story = basic.get('story')
isEReleased = basic.get('isEReleased')
return {'movieId': movieId, 'name': name, 'nameEn': nameEn, 'url': url, 'movieStatus': movieStatus,
'types': types, 'overallRating': overallRating, 'actors': actors, 'director': director,
'writers': writers, 'story': story, 'isEReleased': isEReleased}
except Exception as e:
print(e, value)
return None
数据存储器
import pymongo
class DataOutput(object):
def __init__(self):
client = pymongo.MongoClient('localhost', 27017)
db = client.move
self.collection = db.move_info
self.datas = []
def store_data(self, data):
"""
数据存储
:param data:
:return:
"""
if data is None:
return
self.datas.append(data)
if len(self.datas) > 10:
self.output_db()
def output_db(self):
try:
info_id = self.collection.insert_many(self.datas)
self.datas = []
except Exception as e:
print('新增异常', e)
def conn_end(self):
if len(self.datas) > 0:
self.output_db()
# 关闭数据库
爬虫调度器
import json
import time
from HtmlDownloader import HtmlDownloader
from HtmlParser import HtmlParser
from DataOutput import DataOutput
info_url = 'http://front-gateway.mtime.com/library/movie/detail.api?tt={}&movieId={}&locationId=290'
class SpiderMan(object):
def __init__(self):
self.downloader = HtmlDownloader()
self.parser = HtmlParser()
self.dataOutput = DataOutput()
def crawl(self, root_url):
content = self.downloader.download(root_url)
urls = self.parser.parser_url(root_url, content)
print(urls)
show_urls = urls['show_urls']
future_urls = urls['future_urls']
for moveId in show_urls:
try:
url = info_url.format(int(time.time()), moveId)
content_info = self.downloader.infoDownload(url)
# 基本信息
result = self.parser.parser_json(url, content_info)
self.dataOutput.store_data(result)
except Exception as e:
print('Crawl failed')
for moveId in future_urls:
try:
url = info_url.format(int(time.time()), moveId)
content_info = self.downloader.infoDownload(url)
# 基本信息
result = self.parser.parser_json(url, content_info)
self.dataOutput.store_data(result)
except Exception as e:
print('Crawl failed_1')
self.dataOutput.conn_end()
print('Crawl finish')
if __name__ == '__main__':
spider = SpiderMan()
spider.crawl('http://www.mtime.com/')