目录
前言:
爬虫代码与讲解
实现功能: 爬top250,存入mysql 数据库。效果图:
编辑
编辑
一、分析
1.1 爬虫分析
爬虫三步骤:
- 数据获取
- 数据清洗
- 数据存储
数据获取
,用 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(" ", "")
else:
englishName = "-"
otherName = re.findall('<span class="other">(.*?)</span>', info)[0].strip("/").replace(" ", "")
# 评分, 评价人数
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(" ")
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(" ", "")
else:
englishName = "-"
otherName = re.findall('<span class="other">(.*?)</span>', info)[0].strip("/").replace(" ", "")
# 评分, 评价人数
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(" ")
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 ,请先安装,开启数据库。
四、结尾
欢迎探讨