目录
index_url(url, header):获取指定 URL 的页面内容
parse_url(html):从页面内容中提取电影详情页的链接
parse_detral(detail_html):从电影详情页的 HTML 内容中提取电影相关信息
1.简介
1.1项目背景和目标
项目目标:从豆瓣电影网站上爬取正在热映电影的相关信息:
(网址:https://movie.douban.com/cinema/nowplaying/liuzhou/),这里我以柳州为例子,包括电影名字、演员、类型、语言、上映日期和简介,并且存储到mongodb数据库中
1.2技术栈概述
技术栈:依赖 requests 库发送 HTTP 请求,logging 库记录日志信息,re 库进行正则表达式匹配
2.数据爬取流程
2.1 前期准备工作
当进行数据爬取项目时,我们需要导入一些常用的库和模块来辅助我们完成任务。以下是一些常见的库和模块:
requests
:用于发送HTTP请求并获取网页内容。logging
:用于记录日志信息,方便调试和追踪程序运行情况。re
:提供正则表达式匹配功能,可以方便地从文本中提取所需信息。pymongo
:用于连接MongoDB数据库,并进行数据的插入、查询等操作。
import requests
import logging
import re
from pymongo import MongoClient
配置日志记录是在编程过程中非常重要的一环,它可以帮助我们追踪程序的运行情况、排查问题,并且记录下程序的运行日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s = %(levelname)s: %(message)s')
2.2 发送 HTTP 请求获取网页内容
首先我们先定义好根网址以及请求头用来模拟浏览器,先想办法获取到根网址的html代码,才能对后续操作做好准备,图中的index_url是一个通用的解析函数,可以进行根网址以及电影详情链接的解析
# 定义豆瓣电影正在热映页面的 URL
BASE_URL = 'https://movie.douban.com/cinema/nowplaying/liuzhou/'
# 设置 HTTP 请求头,模拟浏览器发送请求
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0'
}
# 获取指定 URL 的页面内容
def index_url(url, header):
logging.info('scrape %s...', url)
try:
response = requests.get(url=url, headers=header)
if response.status_code == 200:
response.encoding = 'utf-8'
return response.text
else:
logging.info('获取失败')
return ''
except requests.RequestException:
logging.error('error occurred while scraping %s', url)
return ''
3.提取电影详情页链接
将上述获得根网址的html代码作为参数传到此函数中,图中代码的作用是利用正则表达式在根目录的html代码中寻找到每部电影的详情链接,得到一个detail_url列表
# 从页面内容中提取电影详情页的链接
def parse_url(html):
pattern = re.compile('<li class="poster".*?<a href="(.*?)".*?>.*?</li>', re.S)
items = re.findall(pattern, html)
for i in items:
detail_url = i
logging.info('得到详情链接%s', detail_url)
yield detail_url
4.获取电影详情页内容解析电影详情页并提取相关信息
将解析得到的详情页的html代码作为参数传递到下图函数中,使用正则表达式寻找我们想要的信息
# 从电影详情页的 HTML 内容中提取电影相关信息
def parse_detral(detail_html):
movie_name_pattern = re.compile('<span\sproperty="v:itemreviewed">(.*?)</span>', re.S)
actor_pattern = re.compile('<a href=".*?" rel="v:starring">(.*?)</a>', re.S)
movie_type_pattern = re.compile('<span property="v:genre">(.*?)</span>', re.S)
language_pattern = re.compile('语言:</span>(.*?)<br/>', re.S)
public_date_pattern = re.compile('<span property="v:initialReleaseDate" content=".*?">(.*?)</span>', re.S)
intro_pattern = re.compile('<span property="v:summary" class="">(.*?)</span>', re.S)
movie_name = re.search(movie_name_pattern, detail_html).group(1).strip() if re.search(
movie_name_pattern, detail_html) else None
actor = re.findall(actor_pattern, detail_html) if re.findall(actor_pattern, detail_html) else None
movie_type = re.findall(movie_type_pattern, detail_html) if re.findall(movie_type_pattern, detail_html) else None
language = re.search(language_pattern, detail_html).group(1).strip() if re.search(
language_pattern, detail_html) else None
public_date = re.search(public_date_pattern, detail_html).group(1).strip() if re.search(
public_date_pattern, detail_html) else None
intro = re.search(intro_pattern, detail_html).group(1).strip() if re.search(
intro_pattern, detail_html) else None
return {
'movie_name': movie_name,
'actor': actor,
'movie_type': movie_type,
'language': language,
'public_date': public_date,
'intro': intro
}
4.数据库连接以及相关配置
对数据库进行连接,在下面代码中可以修改自己的数据库名字以及集合名称,localhost就是自己电脑的ip,无需修改,需要注意的是,mongodb数据库的端口号是27017,也是不需要修改的
# MongoDB 连接配置
MONGO_HOST = 'localhost'
MONGO_PORT = 27017
MONGO_DB = 'douban_movie'
MONGO_COLLECTION = 'movies'
# 连接 MongoDB 数据库
client = MongoClient(MONGO_HOST, MONGO_PORT)
db = client[MONGO_DB]
collection = db[MONGO_COLLECTION]
5.主函数的实现
main():调用关键函数实现整个流程
# 主函数
def main():
html = index_url(BASE_URL, header)
detail_urls = parse_url(html)
for detail_url in detail_urls:
try:
detail_html = index_url(detail_url, header)
if detail_html:
data = parse_detral(detail_html)
logging.info("得到电影相关信息:%s", data)
# 将电影信息插入数据库
collection.insert_one(data)
else:
logging.info("获取电影详情失败:%s", detail_url)
except requests.exceptions.Timeout:
logging.info("获取电影详情失败:%s", detail_url)
continue
if __name__ == '__main__':
main()
6.关键函数详解
index_url(url, header):获取指定 URL 的页面内容
parse_url(html):从页面内容中提取电影详情页的链接
parse_detral(detail_html):从电影详情页的 HTML 内容中提取电影相关信息
7.完整代码过程
完整代码展示如下:
# 导入需要的库和模块
import requests
import logging
import re
from pymongo import MongoClient
# 配置日志记录
logging.basicConfig(level=logging.INFO, format='%(asctime)s = %(levelname)s: %(message)s')
# 定义豆瓣电影正在热映页面的 URL
BASE_URL = 'https://movie.douban.com/cinema/nowplaying/liuzhou/'
# 设置 HTTP 请求头,模拟浏览器发送请求
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0'
}
# 获取指定 URL 的页面内容
def index_url(url, header):
logging.info('scrape %s...', url)
try:
response = requests.get(url=url, headers=header)
if response.status_code == 200:
response.encoding = 'utf-8'
return response.text
else:
logging.info('获取失败')
return ''
except requests.RequestException:
logging.error('error occurred while scraping %s', url)
return ''
# 从页面内容中提取电影详情页的链接
def parse_url(html):
pattern = re.compile('<li class="poster".*?<a href="(.*?)".*?>.*?</li>', re.S)
items = re.findall(pattern, html)
for i in items:
detail_url = i
logging.info('得到详情链接%s', detail_url)
yield detail_url
# 从电影详情页的 HTML 内容中提取电影相关信息
def parse_detral(detail_html):
movie_name_pattern = re.compile('<span\sproperty="v:itemreviewed">(.*?)</span>', re.S)
actor_pattern = re.compile('<a href=".*?" rel="v:starring">(.*?)</a>', re.S)
movie_type_pattern = re.compile('<span property="v:genre">(.*?)</span>', re.S)
language_pattern = re.compile('语言:</span>(.*?)<br/>', re.S)
public_date_pattern = re.compile('<span property="v:initialReleaseDate" content=".*?">(.*?)</span>', re.S)
intro_pattern = re.compile('<span property="v:summary" class="">(.*?)</span>', re.S)
movie_name = re.search(movie_name_pattern, detail_html).group(1).strip() if re.search(
movie_name_pattern, detail_html) else None
actor = re.findall(actor_pattern, detail_html) if re.findall(actor_pattern, detail_html) else None
movie_type = re.findall(movie_type_pattern, detail_html) if re.findall(movie_type_pattern, detail_html) else None
language = re.search(language_pattern, detail_html).group(1).strip() if re.search(
language_pattern, detail_html) else None
public_date = re.search(public_date_pattern, detail_html).group(1).strip() if re.search(
public_date_pattern, detail_html) else None
intro = re.search(intro_pattern, detail_html).group(1).strip() if re.search(
intro_pattern, detail_html) else None
return {
'movie_name': movie_name,
'actor': actor,
'movie_type': movie_type,
'language': language,
'public_date': public_date,
'intro': intro
}
# MongoDB 连接配置
MONGO_HOST = 'localhost'
MONGO_PORT = 27017
MONGO_DB = 'douban_movie'
MONGO_COLLECTION = 'movies'
# 连接 MongoDB 数据库
client = MongoClient(MONGO_HOST, MONGO_PORT)
db = client[MONGO_DB]
collection = db[MONGO_COLLECTION]
# 其他代码保持不变...
# 主函数
def main():
html = index_url(BASE_URL, header)
detail_urls = parse_url(html)
for detail_url in detail_urls:
try:
detail_html = index_url(detail_url, header)
if detail_html:
data = parse_detral(detail_html)
logging.info("得到电影相关信息:%s", data)
# 将电影信息插入数据库
collection.insert_one(data)
else:
logging.info("获取电影详情失败:%s", detail_url)
except requests.exceptions.Timeout:
logging.info("获取电影详情失败:%s", detail_url)
continue
if __name__ == '__main__':
main()
请注意,这只是一个示例目录,具体的博客目录可以根据你的实际内容和结构进行调整
8.Python爬虫实战项目总结
在完成这个豆瓣电影数据爬取实战项目的过程中,我获得了以下几点心得体会: 网页数据的获取:通过 requests 库可以方便地获取网页内容,而且可以设置请求头信息来模拟浏览器发送请求,更好地应对反爬虫措施。 数据提取与处理:利用正则表达式可以很好地从网页内容中提取所需的信息,但也需要注意不同网页结构可能导致匹配规则的变化,需要灵活应对。 日志记录的重要性:在项目开发过程中,合理地使用 logging 库记录日志信息可以帮助我们实时监控程序运行情况,快速定位问题并进行调试。 异常处理的必要性:网络请求可能会出现超时、连接断开等异常情况,合理的异常处理能够保证程序的稳定性和健壮性。 总的来说,通过这个实战项目,我对 Python 爬虫的开发流程有了更深入的理解,也更加熟悉了相关的库和模块的使用。同时,我意识到在实际项目中,除了编写代码外,良好的日志记录和异常处理也是非常重要的。希望通过不断地实践和学习,能够进一步提升自己在爬虫领域的技能水平。