python-动态爬虫实例笔记

动态网站抓取

动态网页的抓取比静态网页困难一些,主要涉及到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的值就行
在这里插入图片描述

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/')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值