利用requests库与lxml库抓取电影数据网站

简单概述

本系列可能是一个比较长的系列,主要是对《Python3网络爬虫开发实战》前七章的一个内容总结并且熟悉使用一下相关的框架与技术。

任务目标

爬取电影数据网站https://ssr1.scrape.center/,此网站无反爬,数据通过服务端渲染,需要爬取的部分为列表页里面的电影数据详情。

任务目标解析

  1. 爬取https://ssr1.scrape.center/网站的列表页面,通过列表页面的内容获取到需要的URL
  2. 爬取https://ssr1.scrape.center/detail/{id}网站内的数据详情,需要获取的部分有:
    1. 电影标题
    2. 电影图片的url
    3. 电影上映时间
    4. 电影分类
    5. 电影评分
    6. 剧情简介
  3. 将内容存放到需要的数据库中

技术选型与爬取

如何爬取

本次爬取选择使用在urllib库的基础上诞生的requests库,此库对urllib库的部分功能进行了重新的封装和扩展,使得网页爬取变得更加的容易。

构建基础的爬取函数

这部分的爬取函数的构建同之前使用urllib库构建的基本类似,但requests库对其进行了一些扩展,可以更好的做一些请求操作。

def scrape_page(url):
    logging.info('scraping url: %s', url)
    # 进行异常操作
    try:
        response = requests.get(url=url)
        if response.status_code == 200:
            return response.text
        logging.error('get invalid status code %s while scraping %s',
                      response.status_code, url)
    except requests.RequestException:
        logging.error('error occured while scraping %s', url, exc_info=True)

构建列表页的爬取函数

在基础的爬取函数上进行相关内容的爬取,因此这里只需要更改url后直接调用爬取函数即可。

# 爬取列表页
def scrape_index(page):
    index_url = f'{BASE_URL}/page/{page}'
    return scrape_page(index_url)

构建详情页的爬取函数

由于列表页解析后会获得所需要的url,因此我们直接调用url进行抓取页面即可。

# 爬取详情页
def scrape_detail(url):
    return scrape_page(url)

如何解析

xpath规则解析

本次使用的方式不再是使用最基本的正则表达式进行解析,虽然正则表达式在构建后能够准确的找到我们所需要的内容,但是构建一个正则表达式还是比较繁琐的,因此这里使用了xpath规则进行网页节点的提取,可以利用xpath教程简单快速的学习使用xpath。

解析列表页

在之前使用re模块进行列表解析时,我们需要构建一个正则表达式,实际上利用正则表达式,其实是对获取的网页进行了字符串匹配,因此当出现多个节点层没有办法找到容易辨识的特征时,构建正则表达式其实会比较麻烦,而使用xpath规则时就直接变得很简单,很容易就能够获取到我们需要的内容:

# 解析列表页
def parse_index(html_str):
    html = etree.HTML(html_str)
    # 解析获取需要的内容
    items = html.xpath('//a[@class="name"]/@href')
    if not items:
        return []
    for item in items:
        # 进行链接的拼接
        detail_url = urljoin(BASE_URL, item)
        # 输出生成的链接信息
        logging.info('get detail url %s', detail_url)
        # 生成器函数
        yield detail_url

解析详情页

详情页的构建在之前需要多个正则表达式进行构建,而使用xpath规则时构建就容易许多,可以利用元素标签名+类名快速获取到我们所需要的内容,但re模块也不是没有特殊的用武之地,对于本次要抓取的电影网站来说,如果要获取到电影的上映时间,此时利用re模块获取速度能够特别的快。

# 解析详情页
def parse_detail(html_str, index):
    html = etree.HTML(html_str)

    # 电影标题
    name = html.xpath('//h2[@class="m-b-sm"]/text()')[0] if html.xpath('//h2[@class="m-b-sm"]/text()') \
            else None
    # 电影图片URL
    cover = html.xpath('//img[@class="cover"]/@src')[0] if html.xpath('//img[@class="cover"]/@src') \
            else None
    # 电影上映时间
    published_at = re.compile('(\d{4}-\d{2}-\d{2}) 上映')
    published = re.search(published_at, html_str).group(1) if re.search(published_at, html_str) \
                else None
    # 电影分类
    categories = html.xpath('//button[contains(@class, "category")]/span/text()') \
                if html.xpath('//button[contains(@class, "category")]/span/text()') \
                else None
    # 电影评分
    score = html.xpath('//p[contains(@class, "score")]/text()')[0] \
            if html.xpath('//p[contains(@class, "score")]/text()') \
            else None
    # 电影剧情简介
    drama = html.xpath('//div[contains(@class, "drama")]/p/text()')[0] \
            if html.xpath('//div[contains(@class, "drama")]/p/text()') \
            else None
    
    # 以字典形式返回所需要的内容
    return {
        'index': index,
        'name': name,
        'cover': cover,
        'published': published,
        'categories': categories,
        'drama': drama.strip(),
        'score': score.strip()
    }

如何存储

本次存储使用了JSON格式的存储方式,由于我们获取数据时为字典,通过json模块能够十分便捷快速

的将数据存储,但是如果要在一个文件内存储,处理[]就不算太容易了,因此我们可以在完成此任务后手动添加。

# 以json文件的方式进行存储
def save_data(data):
    data_path = '{0}/movies.json'.format(RESULT_DIR)

    # 创建一个进行内容的存放
    with open(data_path, 'a+', encoding='utf-8') as file:
        json.dump(data, file, ensure_ascii=False, indent=2)
        file.write(',')
        file.write('\n')

附录-源代码

import json
import re
import requests
import logging

from os import makedirs
from os.path import exists
from lxml import etree
from urllib.parse import urljoin

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(levelname)s: %(message)s')

# 常量
BASE_URL = 'https://ssr1.scrape.center'
TOTAL_PAGE = 10
RESULT_DIR = 'results'

exists(RESULT_DIR) or makedirs(RESULT_DIR)


# 网页内容的爬取

# 网页爬取
def scrape_page(url):
    logging.info('scraping url: %s', url)
    # 进行异常操作
    try:
        response = requests.get(url=url)
        if response.status_code == 200:
            return response.text
        logging.error('get invalid status code %s while scraping %s',
                      response.status_code, url)
    except requests.RequestException:
        logging.error('error occured while scraping %s', url, exc_info=True)

# 爬取列表页
def scrape_index(page):
    index_url = f'{BASE_URL}/page/{page}'
    return scrape_page(index_url)


# 爬取详情页
def scrape_detail(url):
    return scrape_page(url)


# 网页内容的解析

# 解析列表页
def parse_index(html_str):
    html = etree.HTML(html_str)
    # 解析获取需要的内容
    items = html.xpath('//a[@class="name"]/@href')
    if not items:
        return []
    for item in items:
        # 进行链接的拼接
        detail_url = urljoin(BASE_URL, item)
        # 输出生成的链接信息
        logging.info('get detail url %s', detail_url)
        # 生成器函数
        yield detail_url


# 解析详情页
def parse_detail(html_str, index):
    html = etree.HTML(html_str)

    # 电影标题
    name = html.xpath('//h2[@class="m-b-sm"]/text()')[0] if html.xpath('//h2[@class="m-b-sm"]/text()') \
            else None
    # 电影图片URL
    cover = html.xpath('//img[@class="cover"]/@src')[0] if html.xpath('//img[@class="cover"]/@src') \
            else None
    # 电影上映时间
    published_at = re.compile('(\d{4}-\d{2}-\d{2}) 上映')
    published = re.search(published_at, html_str).group(1) if re.search(published_at, html_str) \
                else None
    # 电影分类
    categories = html.xpath('//button[contains(@class, "category")]/span/text()') \
                if html.xpath('//button[contains(@class, "category")]/span/text()') \
                else None
    # 电影评分
    score = html.xpath('//p[contains(@class, "score")]/text()')[0] \
            if html.xpath('//p[contains(@class, "score")]/text()') \
            else None
    # 电影剧情简介
    drama = html.xpath('//div[contains(@class, "drama")]/p/text()')[0] \
            if html.xpath('//div[contains(@class, "drama")]/p/text()') \
            else None
    
    # 以字典形式返回所需要的内容
    return {
        'index': index,
        'name': name,
        'cover': cover,
        'published': published,
        'categories': categories,
        'drama': drama.strip(),
        'score': score.strip()
    }

# 存储获取到的数据

# 以json文件的方式进行存储
def save_data(data):
    data_path = '{0}/movies.json'.format(RESULT_DIR)

    # 创建一个进行内容的存放
    with open(data_path, 'a+', encoding='utf-8') as file:
        json.dump(data, file, ensure_ascii=False, indent=2)
        file.write(',')
        file.write('\n')
        
# 主函数
def main():
    index = 0
    for page_index in range(1, TOTAL_PAGE + 1):
        # 获取列表页
        page_html = scrape_index(page=page_index)
        # 获取详情页
        detail_urls = parse_index(page_html)
        for detail_url in detail_urls:
            index += 1
            # 抓取详情页内容
            detail_html = scrape_detail(detail_url)
            # 获取数据
            data = parse_detail(detail_html, index)
            # 获取信息
            logging.info('get detail data %s', data)
            save_data(data=data)


if __name__ == '__main__':
    main()

版权信息

本文由PorterZhang整理或写作完成
本人的Github: PorterZhang2021
本人的博客地址:PorterZhang

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Python的lxml是一个用于解析XML和HTML文档的扩展,即使处理的标签非常混乱,也能快速解析。你可以使用pip install lxml命令来安装lxml。\[1\] 而requests是一个用于发送HTTP请求的,相比内建的urllib2模块,它具有更快的速度和更好的可读性。你可以使用pip install requests命令来安装requests。\[1\] 使用requests的get方法可以发送GET请求,通过添加headers参数可以模拟浏览器发送请求,以防止爬取不到内容。\[2\] 下一步,我们可以使用requests.get方法从网页中获取数据,并使用lxml解析它。将解析结果保存在tree中,以便进一步处理。\[3\] #### 引用[.reference_title] - *1* *3* [Python语言使用lxml模块和Requests模块抓取HTML页面的教程](https://blog.csdn.net/weixin_39890543/article/details/110786861)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Python lxml 提取并保存网页正文部分](https://blog.csdn.net/qfcy_/article/details/119817805)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值