爬取豆瓣Top250信息,存入mysql数据库(附全部源码)

176 阅读6分钟

目录

前言:

一、分析

1.1 爬虫分析

1.2 网站分析

1.2.1 判断网页类型

1.2.2 url及其参数

二、爬虫

2.1 数据获取 requests

2.2 数据清洗 re

2.3 数据存储 mysql

2.4 运行

三、完整代码

四、结尾


前言:

爬虫代码与讲解

实现功能: 爬top250,存入mysql 数据库。效果图:

​编辑

​编辑


一、分析

1.1 爬虫分析

爬虫三步骤:

  1. 数据获取
  2. 数据清洗
  3. 数据存储

数据获取,用 requests 库,urllib 库等。本文用 requests演示 数据清洗,有 xpath、bs4、re正则、pyquery 等,本文用 正则(re)演示 数据存储,有 txt、csv(csv库)、xlsx(pandas库或)、数据库(mysql,mongdb等),本文用 mysql数据库(pymysql)演示


1.2 网站分析

搜索引擎输入 top250

​编辑


1.2.1 判断网页类型

通过 ctrl+U 查看网页源代码

​编辑 可以发现,电影数据都在 html 里面。因此,该网页是静态网页。如果是动态网页,需要解析真实地址抓取,或者用 selenium


1.2.2 url及其参数

抓包 f12 是浏览器自带的抓包工具 通过 右键->检查->network(或网络)->ctrl+r刷新,可以看到刷过很多文件。可以找到网页的 html 文件。

​编辑

在 header 里面,可以看到请求头相关信息,比如 请求 url,cookie,user-agent,request-method等。

请求方式是 get 请求。get 和 post 请求在爬虫中常用到

user-agent 是浏览器信息。比如上面的 user-agent是

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36

里面包含了浏览器的信息。如果是手机的话或者linux,或者谷歌浏览器,里面会有 chrome,linux等。通常,网站会检查请求的 user-agent,防止爬虫程序获取数据。

当爬网站发现,响应数据不正常的时候,可以添加 user-agent 和 cookie 尝试

url:豆瓣电影 Top 250 该 url 里面包含了两个参数 start 和 filter 眼尖的朋友可能注意到了,start 表示开始,filter 表示过滤  ,当我们点亮 我没看过的后,刷新可以看到,filter 变成了 unwatched。而点击下一页,可以爬到,start 变成了25 所以,可以通过修改 start 的值来翻页。而 filter 参数没太大用处


二、爬虫

2.1 数据获取 requests

python的强大之处,其中一点是第三方库很完善。 首先创造一个 DouBan 类

class DouBan:
    def __init__(self, user, password, host='localhost', port=3306):
        self.url = "https://movie.douban.com/top250?start={}&filter="
        self.headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"}

通过 requests 库获取数据

    def dataGet(self, start):
        return requests.get(self.url.format(start), headers=self.headers).text

该函数,首先接收页面 start,通过 requests 的 get 方法,返回 html 的字符串


2.2 数据清洗 re

def dataClean(self, html:str):
        result = []
        # 将 换行符替换掉
        html = html.replace("\n", "")
        # rep 是匹配每个电影的数据,再用 for 循环,逐个清洗数据
        rep = re.compile('<div class="info">.*?</li>')
        for info in re.findall(rep, html):
            # 名字
            names = re.findall('<span class="title">(.*?)</span>', info)
            chineseName = names[0]
            if len(names)==2:
                englishName = names[1].replace("/", "").strip().replace("&nbsp;", "")
            else:
                englishName = "-"
            otherName = re.findall('<span class="other">(.*?)</span>', info)[0].strip("/").replace("&nbsp;", "")
            # 评分, 评价人数
            star = re.findall('<span class="rating_num".*?>(.*?)</span>', info)[0]
            starPeople = re.findall('<span>(.*?)人评价</span>', info)[0]
            starPeople = self.Format(starPeople)
            # 语录
            try:
                nidehanwang = re.findall('<span class="inq">(.*?)</span>', info)[0]
            except:
                nidehanwang = "-"
            # 其余数据
            others = re.findall('<p class="">(.*?)</p>', info)[0].strip().replace("/", "").split("&nbsp;&nbsp;")
            type = others[-1]
            country = others[-2]
            if len(others) == 4:
                direct = others[0].split(":")[-1].strip()
                others = others[1].split("<br>")
                year = others[-1].strip()
                actors = others[0].split(":")[-1]
            else:
                if "主演" not in others[0]:
                    direct = others[0].split("<br>")[0].split(":")[1]
                    year = others[0].split("<br>")[1].strip()
                    actors = "-"
            if len(str(year))>4:
                year = re.findall("([0-9]+)", year)[-1]
            result.append([chineseName, englishName, otherName,
                          direct, actors, year, country, type,
                          star, starPeople, nidehanwang])
        return result
​
    # 数字太长的,改为 万 为单位
    def Format(self, num):
        if len(str(num))>4:
            return str(round(int(num)/10000, 1))+"万"

2.3 数据存储 mysql

如果不想用 mysql ,也可以选择别的存储方式。比如 xlsx,csv,txt,或者其他的数据库

python连接mysql数据库,用 pymysql 库。要经过:连接数据库->建表->存储->关闭连接

连接并建表可以一步完成。因为我们只建一次表,只连一次库

 def sqlConnect(self, user, password, host, port):
        try:
            self.conn = pysql.connect(user=user, password=password, host=host, port=port)
        except Exception as e:
            print("数据库连接失败")
            exit(0)
        print("数据库连接成功!")
        self.cursor = self.conn.cursor()
​
        self.cursor.execute("create database if not exists douban")
        self.cursor.execute("use douban")
        try:
            self.cursor.execute("drop table douban")
        except:
            pass
        sql = '''
                create table douban (
                id int unsigned auto_increment,
                `中文名` varchar(15),
                `英文名` varchar(70),
                `别名` varchar(100),
                `导演` varchar(70),
                `演员` varchar(50),
                `上映年份` int,
                `上映国家` varchar(30),
                `类型` varchar(30),
                `评分` char(3),
                `评价人数` varchar(10),
                `语录` varchar(50),
                primary key (`id`))
            '''
        self.cursor.execute(sql)

存储

    def sqlSave(self, datas:list):
        sql = '''
                insert into douban (
                `中文名`, `英文名`, `别名`, `导演`, `演员`, `上映年份`, `上映国家`, `类型`, `评分`, `评价人数`, `语录`
                ) value("{}", "{}", "{}", "{}", "{}", {}, "{}", "{}", "{}", "{}", "{}")
            '''
        for line in datas:
            newsql = sql
            for i in line:
                newsql = newsql.replace("{}", i, 1)
            self.cursor.execute(newsql)
        self.conn.commit()

2.4 运行

    def run(self):
        for i in range(10):
            print("正在爬取第<{}>/<{}>页".format(i, 10))
            html = self.dataGet(i*25)
            data = self.dataClean(html)
            self.sqlSave(data)
        print("爬取成功!")
        self.conn.close()

三、完整代码

# -*- coding: utf-8 -*-
# @Time    : 2023/5/21 18:18
# @Author  : Tuomasi
# @File    : Douban.py
import requests
import re
import pymysql as pysql


class DouBan:
    def __init__(self, user, password, host='localhost', port=3306):
        self.url = "https://movie.douban.com/top250?start={}&filter="
        self.headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"}
        self.sqlConnect(user, password, host, port)

    def dataGet(self, start):
        return requests.get(self.url.format(start), headers=self.headers).text

    def dataClean(self, html:str):
        result = []
        html = html.replace("\n", "")
        rep = re.compile('<div class="info">.*?</li>')
        for info in re.findall(rep, html):
            # 名字
            names = re.findall('<span class="title">(.*?)</span>', info)
            chineseName = names[0]
            if len(names)==2:
                englishName = names[1].replace("/", "").strip().replace("&nbsp;", "")
            else:
                englishName = "-"
            otherName = re.findall('<span class="other">(.*?)</span>', info)[0].strip("/").replace("&nbsp;", "")
            # 评分, 评价人数
            star = re.findall('<span class="rating_num".*?>(.*?)</span>', info)[0]
            starPeople = re.findall('<span>(.*?)人评价</span>', info)[0]
            starPeople = self.Format(starPeople)
            # 语录
            try:
                nidehanwang = re.findall('<span class="inq">(.*?)</span>', info)[0]
            except:
                nidehanwang = "-"
            # other
            others = re.findall('<p class="">(.*?)</p>', info)[0].strip().replace("/", "").split("&nbsp;&nbsp;")
            type = others[-1]
            country = others[-2]
            if len(others) == 4:
                direct = others[0].split(":")[-1].strip()
                others = others[1].split("<br>")
                year = others[-1].strip()
                actors = others[0].split(":")[-1]
            else:
                if "主演" not in others[0]:
                    direct = others[0].split("<br>")[0].split(":")[1]
                    year = others[0].split("<br>")[1].strip()
                    actors = "-"
            if len(str(year))>4:
                year = re.findall("([0-9]+)", year)[-1]
            result.append([chineseName, englishName, otherName,
                          direct, actors, year, country, type,
                          star, starPeople, nidehanwang])
        return result

    def sqlConnect(self, user, password, host, port):
        try:
            self.conn = pysql.connect(user=user, password=password, host=host, port=port)
        except Exception as e:
            print("数据库连接失败")
            exit(0)
        print("数据库连接成功!")
        self.cursor = self.conn.cursor()

        self.cursor.execute("create database if not exists douban")
        self.cursor.execute("use douban")
        try:
            self.cursor.execute("drop table douban")
        except:
            pass
        sql = '''
                create table douban (
                id int unsigned auto_increment,
                `中文名` varchar(15),
                `英文名` varchar(70),
                `别名` varchar(100),
                `导演` varchar(70),
                `演员` varchar(50),
                `上映年份` int,
                `上映国家` varchar(30),
                `类型` varchar(30),
                `评分` char(3),
                `评价人数` varchar(10),
                `语录` varchar(50),
                primary key (`id`))
            '''
        self.cursor.execute(sql)

    def sqlSave(self, datas:list):
        sql = '''
                insert into douban (
                `中文名`, `英文名`, `别名`, `导演`, `演员`, `上映年份`, `上映国家`, `类型`, `评分`, `评价人数`, `语录`
                ) value("{}", "{}", "{}", "{}", "{}", {}, "{}", "{}", "{}", "{}", "{}")
            '''
        for line in datas:
            newsql = sql
            for i in line:
                newsql = newsql.replace("{}", i, 1)
            self.cursor.execute(newsql)
        self.conn.commit()


    def Format(self, num):
        if len(str(num))>4:
            return str(round(int(num)/10000, 1))+"万"

    def run(self):
        for i in range(10):
            print("正在爬取第<{}>/<{}>页".format(i, 10))
            html = self.dataGet(i*25)
            data = self.dataClean(html)
            self.sqlSave(data)
        print("爬取成功!")
        self.conn.close()

if __name__ == '__main__':
    douban = DouBan(user='root', password='123456')
    douban.run()

通常,user 是 root,密码是自己设的。如果您未安装 mysql ,请先安装,开启数据库。


四、结尾

欢迎探讨