爬取豆瓣TOP250并将数据保存为txt文件和csv文件并存入MySQL数据库

项目源码

import requests   # 发起请求的库
from fake_useragent import UserAgent   # 构造user-Agent的库
import mysql.connector   # Python和MySQL连接的库
from typing import NoReturn, Tuple   # 类型标注的库
from lxml import etree   # 使用lxml提取html的库
import re   # Python中的正则表达式库
import csv  # 写入csv文件的库
import time  # 程序休眠库

class bouban_Top250:

    def __init__(self):
        """初始化"""
        self.conn = mysql.connector.connect(  # 构造和mysql的连接
            host='localhost',
            user='root',
            passwd='123456',
            port=3307,
            charset='utf8',
            database='reptile'
        )
        self.my_cursor = self.conn.cursor()  # 创建油标
        self.start_id = 1    # 电影id编号
        self.all_movies_list = []   # 用来存储所有电影信息

    def send_request(self, url: str) -> str:
        """发送get请求,将HTML页面源码返回
        url:str类型,要请求的链接
        return:str类型,请求的链接页面的html源码
        """
        # 使用fake_useragent.UserAgent()构造请求头
        headers = {
            'user-Agent': UserAgent().Chrome,
        }
        # 发起请求
        response = requests.get(url=url, headers=headers)
        # 根据状态码来返回HTML页面源码
        if response.status_code == 200:
            return response.text
        else:
            print('status_code error')
            return '-1'

    def parse_html(self, html: str) -> Tuple[Tuple]:
        """解析html源码
        html:str类型,html页面源码
        return:Tuple[Tuple],元组中的每一个元素也是一个元组,每一个元素对应一个电影的所有信息
        """
        # 初始化lxml
        e = etree.HTML(html, etree.HTMLParser())
        # 获取li标签列表,每一个li标签中包含一个电影的所有信息
        li_list = e.xpath('//ol[@class="grid_view"]/li')
        movies_tuple = []
        # 遍历每一个li标签,对每一个li标签进行解析
        for li in li_list:
            try:
                name = ''.join(li.xpath('./div/div[@class="info"]/div[@class="hd"]/a/span[@class="title"][1]/text()'))
                href = ''.join(li.xpath('./div/div[@class="info"]/div[@class="hd"]/a/@href'))
                performers = ''.join(li.xpath('string(./div/div[@class="info"]/div[@class="bd"]/p[1])'))
                pattern = re.compile('\n+|\s+')
                performers = re.sub(pattern=pattern, repl='', string=performers)
                score = float(''.join(li.xpath('./div/div[@class="info"]/div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()')))
                quote = ''.join(li.xpath('./div/div[@class="info"]/div[@class="bd"]/p[@class="quote"]/span/text()'))
                tem = (self.start_id, name, href, performers, score, quote)
                movies_tuple.append(tem)
                print(self.start_id, name, href, performers, score, quote, sep="  ")
                self.start_id += 1
            except:
                continue
        return tuple(movies_tuple)

    def create_table(self, table_name: str) -> NoReturn:
        """在mysql数据库中创建表
        table_name:str类型,要创建表的表名
        """
        sql_create_table = "create table if not exists {}(\
                               ID int ,\
                               mv_name varchar(50),\
                               href varchar(50),\
                               performers varchar(300),\
                               score decimal(3, 1),\
                               quote varchar(50), \
                               primary key (ID)\
                               ) ENGINE=INNODB DEFAULT CHARSET='utf8mb4' COLLATE='utf8mb4_unicode_ci'".format(table_name)
        self.my_cursor.execute(sql_create_table)

    def insert_to_mysql(self, table_name: str, movies_tuple: Tuple[Tuple]) -> NoReturn:
        """将传入的电影元组中的所有电影插入到表中
        table_name:str类型,要插入信息的表的表名
        movies_tuple:Tuple[Tuple]类型,每一个元素也是元组,其中包含一个电影的所有信息
        """
        sql_insert = "insert into {} values (%s, %s, %s, %s, %s, %s)".format(table_name)
        self.my_cursor.executemany(sql_insert, movies_tuple)
        self.conn.commit()

    def append_to_MV_list(self, movies_tuple: Tuple[Tuple]) -> NoReturn:
        """将电影元组中的所有电影添加到所有电影列表
        movies_tuple:Tuple[Tuple]类型,要加入的电影元组
        """
        for movie in movies_tuple:
            self.all_movies_list.append(movie)

    def writer_into_csv(self, file_name: str, movies_tuple: Tuple[Tuple]) -> NoReturn:
        """将传入的电影元组中的电影写入csv文件
        file_name:str类型,要创建的csv文件的文件名
        movies_tuple:Tuple[Tuple]类型,要写入的电影元组
        """
        headers = ['编号', '电影名称', '电影链接', '演员信息', '豆瓣评分', '引述']
        with open(file_name, 'w', encoding='utf-8') as f:
            writer = csv.writer(f)
            writer.writerow(headers)
            writer.writerows(movies_tuple)

    def writer_into_txt(self, file_name: str, movies_tuple: Tuple[Tuple]) -> NoReturn:
        """将传入的电影元组写入到txt文件中
        file_name:str类型,要创建的txt文件名
        movies_tuple:Tuple[Tuple]类型,要写入的电影元组
        """
        headers = ['编号', '电影名称', '电影链接', '演员信息', '豆瓣评分', '引述']
        headers = '  '.join(headers)
        with open(file_name, 'w', encoding='utf-8') as f:
            f.write(headers+'\n')
            for movie in movies_tuple:
                movie = list(movie)
                movie[0] = str(movie[0])
                movie[4] = str(movie[4])
                movie = str('  '.join(movie))
                f.write(movie+'\n')

    def get_next_page_url(self, html: str) -> str:
        """从html源码中获取下一页的链接
        html:str类型,获取下一页的html源码
        return:str类型,构造出来的下一页的url
        """
        e = etree.HTML(html, etree.HTMLParser())
        url = ''.join(e.xpath('//span[@class="next"]/a/@href'))
        if url:
            next_page_url = 'https://movie.douban.com/top250{}'.format(url)
            return next_page_url
        else:
            return ''

if __name__ == '__main__':
    """程序入口"""
    bouban = bouban_Top250()
    url = 'https://movie.douban.com/top250?start=0&filter='
    bouban.create_table('douban')
    while url:
        html = bouban.send_request(url)
        url = bouban.get_next_page_url(html)
        movies_tuple = bouban.parse_html(html)
        bouban.append_to_MV_list(movies_tuple)
        time.sleep(1)
    all_movies_tuple = tuple(bouban.all_movies_list)
    bouban.insert_to_mysql('douban', all_movies_tuple)
    bouban.writer_into_txt('douban.txt', all_movies_tuple)
    bouban.writer_into_csv('douban.csv', all_movies_tuple)

准备工作

  • 安装好程序中用到的第三方库
  • 安装好mysql,并将mysql的服务打开
  • 在__init__方法初始化中,将mysql的用户名和密码、数据库更改为自己的,特别是端口号,因为我的电脑中有两个不同的mysql版本,所以我用的端口号是3307
  • 确保自己的电脑有网
  • 认真看代码,注释中都写了代码的功能,有一些代码看不懂要去查一查
  • 我写本文的日期是2021.03 ,豆瓣更新反爬虫可能会导致爬取无效!

结果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

心得

  • re.sub()方法真是坑,它会将替换后的文本返回,不会直接对原来的文本做更改,弄得我花了几十分钟
  • 将文本写入txt文件中不会自动换行,还要自己加换行符,害得我又删除->更改->删除…
  • 将文本写入txt文件中不指定编码方式会导致乱码,我去
  • 你看看下面的结果:
a1 = ((1, 2), (3, 4))
a2 = (('a', 'b'))
a3 = [1, 2]
a4 = [(1, 2)]

a1 + a4 = ?
a4.append(a1)  = ?
a3 + a4 = ?
我又新建了好几个文件来实验,好烦
  • 从编写到完成,我的mysql中的表已经删除了不少于20次
  • 1
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,作为AI语言模型,我无法执行虫操作和访问本地文件系统。但您可以参考以下步骤来完成该任务: 1. 安装Python的虫框架Scrapy和MySQL数据库连接库pymysql: ``` pip install scrapy pip install pymysql ``` 2. 创建一个Scrapy项目: ``` scrapy startproject douban_movie ``` 3. 编写虫代码。在douban_movie/spiders目录下创建一个名为douban_spider.py的文件,实现对豆瓣电影top250页面的,并将结果保存至本地csv文件: ```python import scrapy import csv class DoubanSpider(scrapy.Spider): name = 'douban' start_urls = ['https://movie.douban.com/top250'] def parse(self, response): for movie in response.css('.item'): yield { 'title': movie.css('.title::text').get(), 'rating': movie.css('.rating_num::text').get(), 'comment': movie.css('.quote span::text').get() } next_page = response.css('.next a::attr(href)').get() if next_page is not None: yield response.follow(next_page, self.parse) # 保存至本地csv文件 with open('douban_movie.csv', mode='a', newline='', encoding='utf-8') as file: writer = csv.writer(file) for movie in response.css('.item'): writer.writerow([ movie.css('.title::text').get(), movie.css('.rating_num::text').get(), movie.css('.quote span::text').get() ]) ``` 4. 运行虫并将结果导入MySQL数据库。在douban_movie目录下创建一个名为mysql_pipeline.py的文件,实现将csv文件中的数据导入MySQL数据库: ```python import csv import pymysql class MysqlPipeline: def __init__(self): self.conn = pymysql.connect( host='localhost', port=3306, user='root', password='password', db='douban_movie', charset='utf8mb4' ) self.cursor = self.conn.cursor() def process_item(self, item, spider): self.cursor.execute( "INSERT INTO movie(title, rating, comment) VALUES (%s, %s, %s)", (item['title'], item['rating'], item['comment']) ) self.conn.commit() return item def close_spider(self, spider): self.cursor.close() self.conn.close() if __name__ == '__main__': with open('douban_movie.csv', mode='r', encoding='utf-8') as file: reader = csv.reader(file) next(reader) # 跳过表头 for row in reader: pipeline = MysqlPipeline() pipeline.process_item({ 'title': row[0], 'rating': row[1], 'comment': row[2] }, None) ``` 5. 运行虫并导入数据: ``` scrapy crawl douban python mysql_pipeline.py ``` 注意:在运行mysql_pipeline.py文件之前,需要先创建MySQL数据库和movie表。可以使用以下SQL语句: ``` CREATE DATABASE douban_movie CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE douban_movie; CREATE TABLE movie ( id INT(11) NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, rating FLOAT NOT NULL, comment VARCHAR(255), PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值