爬虫小项目1

爬虫小项目:爬取https://ssr1.scrape.center/

image-20230929172430337

image-20230929172522452

目标:

1.按列表顺序爬取每个电影详情页

2.利用正则提取海报、名称、类别、上映时间、评分、剧情简介

3.将爬取的内容保存下来

网页分析:

image-20230930145223731

海报和标题都有跳转到详情页的链接/detail/1

image-20230930145514680

页码url为/page/3 一共10页

实现

1
2
3
4
1.遍历所有页码,拼接url
2.拿到详情页页面的url
3.在详情页面用正则匹配出需要的内容
4,保存数据

需要匹配详情页的标签:

1
2
3
4
5
<a data-v-7f856186="" href="/detail/1" class="name"></a>
<a data-v-7f856186="" href="/detail/2" class="name"></a>

正则:
<a.*href="(.*?)".*?class="name">

非贪婪模式:

image-20230930154636668

第一步:获取到所有的详情页链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/usr/bin/env python3

import logging
import requests
import re
from urllib.parse import urljoin
import pymongo

#1.配置日志
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')


BASE_URL = "https://ssr1.scrape.center"
TOTAL_PAGE = 10

#2.抓取某一页面的内容
def scrape_index(page):
index_url = f'{BASE_URL}/page/{page}'
return scrape_page(index_url)

#定义函数抓取网页的内容
def scrape_page(url):
logging.info("正在抓取 %s......",url)
#发起get请求
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
else:
logging.error("爬取%s时返回无效的状态码%s" % (url,response.status_code))
except requests.RequestException:
#如果发生异常就报错
# exc_info=True用于日志记录异常信息时,会将异常类型 值 回溯信息 堆栈跟踪等都返回
logging.error("爬取%s发生异常" % url,exc_info=True)

#解析内容,提取出详情页面的url
def parse_index(html):
#使用正则提取详情页面的链接
pattern = re.compile('<a.*href="(.*?)".*?class="name">')
items = re.findall(pattern,html)
#print(items)
if not items:
return []
for item in items:
#把相对链接转为绝对链接
detail_url = urljoin(BASE_URL,item)
#print(detail_url)
logging.info("找到详情页,链接%s"%detail_url)
#返回一个生成器 可以使用for循环或者调用next()方法遍历生成器对象来提取结果
yield detail_url

def main():
for page in range(1,TOTAL_PAGE+1):
#抓取某个页面的内容
index_html = scrape_index(page)
#抓取该页面上跳转详情页的链接
details_urls = parse_index(index_html)
#print(list(details_urls))
logging.info("详情页面链接%s",list(details_urls))

if __name__ == "__main__":
main()

image-20230930161236158

第二步:爬取详情页信息

分析:

1
2
3
4
5
1.图片:img标签
2.类别:两个button里的span标签
3.上映时间:div里的span标签
4.评分:p标签
5.剧情简介:div class=drama里的p标签

image-20230930163136158

正则测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/env python3
#coding:utf-8
import re


#1.url
# txt = '<img data-v-63864230="" src="https://p0.meituan.net/movie/ce4da3e03e655b5b88ed31b5cd7896cf62472.jpg@464w_644h_1e_1c" class="cover">'
# pattern = re.compile('class="el-col.*?<img.*?src="(.*?)".*?class="cover">',re.S)
# response = re.search(pattern,txt).group(1).split('@')
# print(response[0])

#2.种类
# txt = '''
# <div data-v-63864230="" class="categories">

# <button data-v-7f856186="" type="button" class="el-button category el-button--primary el-button--mini">
# <span>剧情</span>
# </button>

# <button data-v-7f856186="" type="button" class="el-button category el-button--primary el-button--mini">
# <span>爱情</span>
# </button>

# </div>
# '''
# #注意这里要加上re.S .任意匹配 包括换行符
# #因为.是匹配除了换行符之外的任意字符
# pattern = re.compile('<button.*?category.*?<span>(.*?)</span>.*?</button>',re.S)
# response = re.findall(pattern,txt)
# print(response)


#3.上映时间
# txt = '''
# <div data-v-7f856186="" class="m-v-sm info">

# <span data-v-7f856186="">1993-07-26 上映</span>

# </div>
# '''
# pattern = re.compile('\d{4}-\d{2}-\d{2} 上映')
# response = re.search(pattern,txt).group(0).split(' ')
# print(response[0])

#评分
# txt = '''
# <div data-v-63864230="" class="el-col el-col-24 el-col-xs-8 el-col-sm-4"><p data-v-63864230=""
# class="score m-t-md m-b-n-sm">
# 9.5</p>
# '''
# pattern = re.compile('<p.*?score.*?>(.*?)</p>',re.S)
# response = re.search(pattern,txt).group(1).split()
# print(response[0])


#剧情简介
txt = '''
<div data-v-63864230="" class="drama"><h3 data-v-63864230="">剧情简介</h3>
<p data-v-63864230="">
影片借一出《霸王别姬》的京戏,牵扯出三个人之间一段随时代风云变幻的爱恨情仇。段小楼(张丰毅 饰)与程蝶衣(张国荣 饰)是一对打小一起长大的师兄弟,两人一个演生,一个饰旦,一向配合天衣无缝,尤其一出《霸王别姬》,更是誉满京城,为此,两人约定合演一辈子《霸王别姬》。但两人对戏剧与人生关系的理解有本质不同,段小楼深知戏非人生,程蝶衣则是人戏不分。段小楼在认为该成家立业之时迎娶了名妓菊仙(巩俐 饰),致使程蝶衣认定菊仙是可耻的第三者,使段小楼做了叛徒,自此,三人围绕一出《霸王别姬》生出的爱恨情仇战开始随着时代风云的变迁不断升级,终酿成悲剧。
</p></div>
'''
pattern = re.compile('<div.*?drama.*?>.*?<p.*?>(.*?)</p>',re.S)
response = re.search(pattern,txt).group(1).strip()
print(response)

#名称
# txt = '''
# <div data-v-63864230="" class="p-h el-col el-col-24 el-col-xs-16 el-col-sm-12">
# <a data-v-63864230=""
# class="router-link-exact-active router-link-active">
# <h2 data-v-63864230="" class="m-b-sm">霸王别姬 - Farewell My Concubine</h2></a>
# '''
# pattern = re.compile('<h2.*?m-b-sm.*?>(.*?)</h2>',re.S)
# response = re.search(pattern,txt).group(1)
# print(response)

实现获取指定页每个电影详情页信息的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env python3
#coding:utf-8

import logging
import requests
import re
from urllib.parse import urljoin
import pymongo

#1.配置日志
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')


BASE_URL = "https://ssr1.scrape.center"
TOTAL_PAGE = 10

#2.抓取某一列表页面的内容 返回html源码
def scrape_index(page):
index_url = f'{BASE_URL}/page/{page}'
return scrape_page(index_url)

#定义函数抓取网页的内容 返回html源码
def scrape_page(url):
logging.info("正在抓取 %s......",url)
#发起get请求
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
else:
logging.error("爬取%s时返回无效的状态码%s" % (url,response.status_code))
except requests.RequestException:
#如果发生异常就报错
# exc_info=True用于日志记录异常信息时,会将异常类型 值 回溯信息 堆栈跟踪等都返回
logging.error("爬取%s发生异常" % url,exc_info=True)

#解析内容,提取出列表页所有电影的详情页面的url
def parse_index(html):
#使用正则提取详情页面的链接
pattern = re.compile('<a.*href="(.*?)".*?class="name">')
items = re.findall(pattern,html)
#print(items)
if not items:
return []
for item in items:
#把相对链接转为绝对链接
detail_url = urljoin(BASE_URL,item)
#print(detail_url)
logging.info("找到详情页,链接%s"%detail_url)
#返回一个生成器 可以使用for循环或者调用next()方法遍历生成器对象来提取结果
yield detail_url

def scrape_detail(url):
return parse_detail(scrape_page(url))


def parse_detail(html):
#使用正则表达式将详情页相应的内容匹配下来
#图片url 正则
img_pattern = re.compile('class="el-col.*?<img.*?src="(.*?)".*?class="cover">',re.S)
img_url = re.search(img_pattern,html).group(1).strip() if re.search(img_pattern,html) else None
#img_url[0]

#类别 正则
#注意这里要加上re.S .任意匹配 包括换行符
#因为.是匹配除了换行符之外的任意字符
categories_pattern = re.compile('<button.*?category.*?<span>(.*?)</span>.*?</button>',re.S)
categories = re.findall(categories_pattern,html) if re.findall(categories_pattern,html) else None

#上映时间 正则
date_pattern = re.compile('(\d{4}-\d{2}-\d{2})\s?上映')
date = re.search(date_pattern,html).group(1) if re.search(date_pattern,html) else None
#date[0]

#评分 正则
score_pattern = re.compile('<p.*?score.*?>(.*?)</p>',re.S)
score = re.search(score_pattern,html).group(1).split() if re.search(score_pattern,html) else None
#score[0] 返回的列表

#剧情简介 正则
drama_pattern = re.compile('<div.*?drama.*?>.*?<p.*?>(.*?)</p>',re.S)
drama = re.search(drama_pattern,html).group(1).strip() if re.search(drama_pattern,html) else None
#drama[0]

#名称 正则
name_pattern = re.compile('<h2.*?m-b-sm.*?>(.*?)</h2>',re.S)
name = re.search(name_pattern,html).group(1) if re.search(name_pattern,html) else None
#name

return {
# 'image':img_url[0],
# 'categories':categories,
# 'date':date[0],
# 'score':score[0],
# 'drama':drama,
# 'name':name
'image':img_url,
'categories':categories,
'date':date,
'score':score[0],
'drama':drama,
'name':name
}

def main():
#获取所有电影详情页的链接
# for page in range(1,TOTAL_PAGE+1):
# #抓取某个页面的内容 返回html源码
# index_html = scrape_index(page)
# #根据源码抓取该页面上跳转详情页的链接
# details_urls = parse_index(index_html)
# #print(list(details_urls))
# logging.info("详情页面链接%s",list(details_urls))

#详情页信息爬取测试--第一页
#获取到第一页的源码
index_html = scrape_index(2)
#获取第一页所有电影的详情页的url
detail_urls = parse_index(index_html)
#爬取每个详情页的信息
for detail_url in detail_urls:
data = scrape_detail(detail_url)
logging.info("get detail data %s" % data)

if __name__ == "__main__":
main()

image-20230930190440051

第三步 存入数据库(mongodb)+多线程

centos7安装mongodb3.6以上

CentOS7下安装配置Mongodb3.6 - #独狼 - 博客园 (cnblogs.com)

Cenos7 yum安装mongodb以及各种错误的解决办法-CSDN博客

Linux Centos 7安装MongoDB(简单!详细!) - 知乎 (zhihu.com)

image-20230930201309769

下载mongodb compass(gui工具) Documents显示有问题(版本原因)

MongoDB Compass Download (GUI) | MongoDB

存入mongodb

1
2
3
4
5
6
7
8
9
10
11
#创建mongodb客户端
mongo_client = pymongo.MongoClient("mongodb://10.210.100.131:27017/")
#创建数据库
db = mongo_client['r1_movies']
collection = db['movies']

#保存数据到mongodb 使用时调用此函数即可
def save_data(data):
#插入数据
collection.insert_one(data)
logging.info("数据保存到mongodb成功!!!")
image-20230930201234040

image-20230930201813980

多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/env python3
#coding:utf-8

import logging
import requests
import re
from urllib.parse import urljoin
import pymongo
import multiprocessing

#创建mongodb客户端
mongo_client = pymongo.MongoClient("mongodb://10.210.100.131:27017/")
#创建数据库
db = mongo_client['r1_movies']
collection = db['movies']

#1.配置日志
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')


BASE_URL = "https://ssr1.scrape.center"
TOTAL_PAGE = 10

#2.抓取某一列表页面的内容 返回html源码
def scrape_index(page):
index_url = f'{BASE_URL}/page/{page}'
return scrape_page(index_url)

#定义函数抓取网页的内容 返回html源码
def scrape_page(url):
logging.info("正在抓取 %s......",url)
#发起get请求
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
else:
logging.error("爬取%s时返回无效的状态码%s" % (url,response.status_code))
except requests.RequestException:
#如果发生异常就报错
# exc_info=True用于日志记录异常信息时,会将异常类型 值 回溯信息 堆栈跟踪等都返回
logging.error("爬取%s发生异常" % url,exc_info=True)

#解析内容,提取出列表页所有电影的详情页面的url
def parse_index(html):
#使用正则提取详情页面的链接
pattern = re.compile('<a.*href="(.*?)".*?class="name">')
items = re.findall(pattern,html)
#print(items)
if not items:
return []
for item in items:
#把相对链接转为绝对链接
detail_url = urljoin(BASE_URL,item)
#print(detail_url)
logging.info("找到详情页,链接%s"%detail_url)
#返回一个生成器 可以使用for循环或者调用next()方法遍历生成器对象来提取结果
yield detail_url

def scrape_detail(url):
return parse_detail(scrape_page(url))


def parse_detail(html):
#使用正则表达式将详情页相应的内容匹配下来
#图片url 正则
img_pattern = re.compile('class="el-col.*?<img.*?src="(.*?)".*?class="cover">',re.S)
img_url = re.search(img_pattern,html).group(1).strip() if re.search(img_pattern,html) else None
#img_url[0]

#类别 正则
#注意这里要加上re.S .任意匹配 包括换行符
#因为.是匹配除了换行符之外的任意字符
categories_pattern = re.compile('<button.*?category.*?<span>(.*?)</span>.*?</button>',re.S)
categories = re.findall(categories_pattern,html) if re.findall(categories_pattern,html) else None

#上映时间 正则
date_pattern = re.compile('(\d{4}-\d{2}-\d{2})\s?上映')
date = re.search(date_pattern,html).group(1) if re.search(date_pattern,html) else None
#date[0]

#评分 正则
score_pattern = re.compile('<p.*?score.*?>(.*?)</p>',re.S)
score = re.search(score_pattern,html).group(1).split() if re.search(score_pattern,html) else None
#score[0] 返回的列表

#剧情简介 正则
drama_pattern = re.compile('<div.*?drama.*?>.*?<p.*?>(.*?)</p>',re.S)
drama = re.search(drama_pattern,html).group(1).strip() if re.search(drama_pattern,html) else None
#drama[0]

#名称 正则
name_pattern = re.compile('<h2.*?m-b-sm.*?>(.*?)</h2>',re.S)
name = re.search(name_pattern,html).group(1) if re.search(name_pattern,html) else None
#name

return {
# 'image':img_url[0],
# 'categories':categories,
# 'date':date[0],
# 'score':score[0],
# 'drama':drama,
# 'name':name
'image':img_url,
'categories':categories,
'date':date,
'score':score[0],
'drama':drama,
'name':name
}

#保存数据到mongodb
def save_data(data):
#插入数据
collection.insert_one(data)
logging.info("数据保存到mongodb成功!!!")

def main(page):
#获取所有电影详情页的链接
# for page in range(1,TOTAL_PAGE+1):
# #抓取某个页面的内容 返回html源码
# index_html = scrape_index(page)
# #根据源码抓取该页面上跳转详情页的链接
# details_urls = parse_index(index_html)
# #print(list(details_urls))
# logging.info("详情页面链接%s",list(details_urls))

#加入多线程后舍弃
# for page in range(1,TOTAL_PAGE+1):
# #详情页信息爬取测试--第一页
# #获取到第一页的源码
# index_html = scrape_index(page)
# #获取第一页所有电影的详情页的url
# detail_urls = parse_index(index_html)
# #爬取每个详情页的信息
# for detail_url in detail_urls:
# data = scrape_detail(detail_url)
# #logging.info("get detail data %s" % data)
# save_data(data=data)
# logging.info("data save successfully!!!")


#详情页信息爬取测试--第一页
#获取到第一页的源码
index_html = scrape_index(page)
#获取第一页所有电影的详情页的url
detail_urls = parse_index(index_html)
#爬取每个详情页的信息
for detail_url in detail_urls:
data = scrape_detail(detail_url)
#logging.info("get detail data %s" % data)
save_data(data=data)
logging.info("data save successfully!!!")


def run_main(page):
main(page)

if __name__ == "__main__":
#获取CPU核心数量
num_process = multiprocessing.cpu_count()
#根据cpu核心数量创建线程池
pool = multiprocessing.Pool(num_process)
#要爬取页面的数量
page_to_scrape = list(range(1,TOTAL_PAGE+1))
#使用进程池运行
pool.map(run_main,page_to_scrape)
#关闭进程池
pool.close()
#main()
image-20230930204730425 image-20230930204640239

爬虫小项目1
http://example.com/2023/10/07/爬虫小项目1/
作者
r1
发布于
2023年10月7日
许可协议