python利用requests库和html.parser爬取豆瓣电影网并把数据保存到本地的代码,不用beautifulsoup
时间: 2025-06-11 10:55:32 浏览: 14
<think>好的,用户想要使用Python的requests库和html.parser模块来爬取豆瓣电影网站,并且希望将数据保存到本地,同时不使用BeautifulSoup。首先,我需要回忆一下html.parser的基本用法,因为用户明确排除了BeautifulSoup。
首先,我需要确认html.parser是Python标准库中的一个模块,通常在处理HTML时,可以使用html.parser中的HTMLParser类来解析内容。但用户可能指的是不使用第三方解析库,仅用标准库中的工具,比如re模块进行正则表达式匹配,或者结合html.parser来提取数据。
接下来,用户提到要爬取豆瓣电影网站,但豆瓣有反爬虫机制,比如可能需要处理User-Agent、Cookies,或者限速等问题。因此,在代码中需要设置请求头,模拟浏览器访问,避免被拒绝。
然后,数据保存部分,用户希望将数据保存到本地,可能是文本文件或CSV格式。需要确定如何组织爬取的数据,并写入文件。
考虑到用户不使用BeautifulSoup,可能需要用正则表达式来提取所需的信息,比如电影名称、评分、链接等。但正则表达式在处理复杂的HTML结构时可能不够灵活,容易出错,所以需要谨慎设计正则模式。
接下来,我需要检查是否有现成的示例代码或者方法能够满足这些条件。比如,使用requests.get获取网页内容,然后用正则表达式匹配需要的部分,再用html.parser进行解析。不过,html.parser本身是一个解析器,需要继承HTMLParser类并重写方法,这可能比较繁琐,不如正则直接。
可能用户误以为html.parser是一种替代BeautifulSoup的解析方式,但实际上,html.parser的使用方式与BeautifulSoup不同,需要自己编写处理标签的逻辑。因此,可能用户更倾向于使用正则表达式来提取数据,而避免使用任何解析库,包括html.parser。
但根据用户的问题描述,他们明确提到了html.parser模块,因此可能需要结合该模块来解析HTML。例如,自定义一个HTMLParser的子类,重写handle_starttag、handle_data等方法,来抓取特定的标签和内容。
不过,这样的方法相对于正则表达式来说,代码量可能更大,且需要更深入的理解。因此,可能需要权衡使用哪种方式更高效。
此外,豆瓣的电影页面可能包含动态加载的内容,尤其是使用JavaScript渲染的部分,这时候单纯的requests库可能无法获取到完整的数据,因为无法执行JS。用户需要注意这一点,可能只能爬取静态HTML中的内容。
关于数据保存,可能需要将提取的电影信息存储为文本文件,或者CSV格式,或者JSON格式。根据示例,用户可能希望保存为文本或HTML文件,但需要明确用户的需求。
综合以上分析,可能的步骤是:
1. 使用requests库发送HTTP请求,设置合适的请求头(如User-Agent)来绕过豆瓣的反爬虫机制。
2. 获取网页的HTML内容。
3. 使用正则表达式从HTML中提取电影名称、评分、链接等信息。
4. 将提取的数据保存到本地文件,如txt或CSV。
5. 处理可能遇到的异常,如网络错误、请求限制等。
由于用户不想使用BeautifulSoup,所以必须依赖正则表达式或html.parser来解析。但正则可能更直接,而html.parser需要编写更多的解析逻辑。
现在,我需要考虑如何用html.parser来解析特定的标签。例如,假设每个电影条目被包含在某个特定的div标签中,或者特定的class里,那么可以自定义一个解析器来捕获这些数据。
例如,豆瓣电影Top250的页面结构通常每个电影项在<div class="item">中,里面包含标题、评分等信息。因此,可以创建一个HTMLParser的子类,当遇到div标签且class为item时开始记录数据,然后在内部捕获标题、评分等。
不过,这样的实现可能比较复杂,需要处理标签嵌套和数据捕获的状态。相比之下,使用正则表达式可能更简单,但容易受到页面结构变化的影响。
所以,可能的代码结构是:
- 发送请求,获取HTML
- 使用正则表达式匹配每个电影条目
- 在每个条目中,进一步用正则提取标题、评分等
- 保存到文件
或者,使用html.parser来遍历标签,当遇到特定标签和属性时提取数据。
但考虑到用户希望使用html.parser模块,可能需要编写一个解析器类,例如:
from html.parser import HTMLParser
class DoubanParser(HTMLParser):
def __init__(self):
super().__init__()
self.in_title = False
self.current_data = {}
def handle_starttag(self, tag, attrs):
if tag == 'div' and ('class', 'hd') in attrs:
self.in_title = True
# 其他标签处理...
def handle_data(self, data):
if self.in_title:
# 处理数据
pass
不过,这样的实现需要详细分析HTML结构,并根据标签和属性来触发数据捕获,这可能比较繁琐,容易出错,尤其是对于复杂的页面结构。
因此,可能用户更倾向于使用正则表达式,而问题中提到html.parser可能是因为误解,或者希望使用标准库中的工具。但不管怎样,我需要按照用户的要求,使用html.parser模块。
但经过进一步思考,可能用户实际上是想用Python内置的html.parser模块来解析HTML,而不是使用BeautifulSoup。这种情况下,可能需要结合html.parser和正则表达式来提取数据。
例如,使用html.parser来找到包含电影信息的父元素,然后用正则表达式提取具体内容。
但这个过程可能比较复杂,因此可能更简单的方式是直接使用正则表达式匹配整个HTML中的特定模式。
因此,可能示例代码将主要使用正则表达式来提取数据,而html.parser可能并未实际使用,但用户可能希望看到如何结合两者。不过,用户的问题可能允许仅使用requests和正则,而不使用任何解析库,只要不使用BeautifulSoup即可。
但用户明确提到了html.parser模块,所以可能需要使用它。这可能意味着用户希望避免使用第三方解析库,而仅使用标准库中的工具,如html.parser或re。
因此,我需要构造一个示例,其中使用html.parser来解析HTML,并提取所需的数据。
例如,假设电影标题在<span class="title">标签内,评分在<span class="rating_num">中,那么可以创建一个解析器类来捕获这些数据。
但具体实现可能如下:
import requests
from html.parser import HTMLParser
class MovieParser(HTMLParser):
def __init__(self):
super().__init__()
self.movies = []
self.current_movie = {}
self.in_title = False
self.in_rating = False
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
if tag == 'span' and attrs.get('class') == 'title':
self.in_title = True
elif tag == 'span' and attrs.get('class') == 'rating_num':
self.in_rating = True
def handle_data(self, data):
if self.in_title:
self.current_movie['title'] = data.strip()
self.in_title = False
elif self.in_rating:
self.current_movie['rating'] = data.strip()
self.in_rating = False
# 假设在此之后将current_movie添加到movies列表
self.movies.append(self.current_movie)
self.current_movie = {}
def handle_endtag(self, tag):
pass # 可能不需要处理结束标签
然后,使用这个解析器来处理获取的HTML内容:
url = 'https://movie.douban.com/top250'
headers = {'User-Agent': 'Mozilla/5.0'}
response = requests.get(url, headers=headers)
parser = MovieParser()
parser.feed(response.text)
for movie in parser.movies:
print(movie)
但这样的代码可能存在问题,因为实际HTML结构可能更复杂,比如每个电影条目在一个div.item中,而标题可能有多个span(如中文标题和英文标题),因此需要更精确的处理。
此外,评分可能在处理完标题后才会出现,需要确保数据正确对应到每个电影条目。
因此,可能需要调整解析器的逻辑,比如在遇到div.item的开始标签时,初始化一个新的current_movie,然后在其中捕获标题和评分。
修改后的解析器可能如下:
class MovieParser(HTMLParser):
def __init__(self):
super().__init__()
self.movies = []
self.current_movie = {}
self.in_item = False
self.in_title = False
self.in_rating = False
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
if tag == 'div' and attrs.get('class') == 'item':
self.in_item = True
self.current_movie = {}
elif self.in_item:
if tag == 'span' and attrs.get('class') == 'title':
self.in_title = True
elif tag == 'span' and attrs.get('class') == 'rating_num':
self.in_rating = True
def handle_data(self, data):
if self.in_title:
self.current_movie['title'] = data.strip()
self.in_title = False
elif self.in_rating:
self.current_movie['rating'] = data.strip()
self.in_rating = False
def handle_endtag(self, tag):
if tag == 'div' and self.in_item:
self.in_item = False
if self.current_movie: # 确保有数据
self.movies.append(self.current_movie)
这样,每当遇到div.item的开始标签,就初始化一个新的current_movie,并在其中捕获标题和评分。当div.item结束时,将current_movie添加到movies列表。
不过,实际测试中可能需要根据豆瓣的实际HTML结构调整条件,比如class属性可能包含多个值,如class="item clearfix",此时判断条件需要更灵活,例如'item' in attrs.get('class', '').split()。
此外,标题可能有多个span,例如中文标题和英文标题,可能需要处理这种情况,比如只取第一个中文标题,或者合并多个标题。
这可能会增加代码的复杂度,但为了示例的简洁,可能只捕获第一个标题。
此外,豆瓣可能有反爬虫机制,需要设置合适的请求头,如User-Agent,否则可能返回403错误。因此,代码中必须包含headers的设置。
数据保存部分,可以将提取的电影列表写入文本文件或CSV。例如,逐行写入,每行包含电影名称和评分。
综上,完整的示例代码可能如下:
import requests
from html.parser import HTMLParser
class DoubanMovieParser(HTMLParser):
def __init__(self):
super().__init__()
self.movies = []
self.current_movie = {}
self.in_item = False
self.in_title = False
self.in_rating = False
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
# 检测是否进入电影条目
if tag == 'div' and 'class' in attrs and 'item' in attrs['class'].split():
self.in_item = True
self.current_movie = {}
elif self.in_item:
# 捕获标题
if tag == 'span' and 'class' in attrs and attrs['class'] == 'title':
self.in_title = True
# 捕获评分
elif tag == 'span' and 'class' in attrs and attrs['class'] == 'rating_num':
self.in_rating = True
def handle_data(self, data):
if self.in_title:
# 只取第一个中文标题(忽略后面的英文标题)
if 'title' not in self.current_movie:
self.current_movie['title'] = data.strip()
self.in_title = False
elif self.in_rating:
self.current_movie['rating'] = data.strip()
self.in_rating = False
def handle_endtag(self, tag):
if self.in_item and tag == 'div':
# 假设条目结束
self.in_item = False
if self.current_movie.get('title') and self.current_movie.get('rating'):
self.movies.append(self.current_movie)
# 设置请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
# 发送请求
url = 'https://movie.douban.com/top250'
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # 检查请求是否成功
html_content = response.text
# 解析HTML
parser = DoubanMovieParser()
parser.feed(html_content)
# 保存数据到本地
with open('douban_movies.txt', 'w', encoding='utf-8') as f:
for movie in parser.movies:
line = f"标题:{movie['title']},评分:{movie['rating']}\n"
f.write(line)
print('数据保存成功')
except requests.exceptions.RequestException as e:
print(f'请求出错:{e}')
except Exception as e:
print(f'发生错误:{e}')
不过,这个代码可能存在一些问题,比如:
1. 豆瓣Top250页面可能有分页,此代码仅处理了第一页。
2. HTML结构可能变化,导致解析失败。
3. 反爬虫机制可能阻止请求,需要更复杂的处理,如使用代理或cookies。
4. 标题可能有多个span,导致数据重复或被覆盖。
此外,用户可能希望爬取的是最新的电影列表,而非Top250,但问题中没有明确说明,所以假设是Top250。
根据引用[3],豆瓣可能有反爬虫机制,如动态加载内容,但上述代码只能处理静态HTML。因此,在代码中需要提醒用户注意这一点,可能无法获取JavaScript生成的内容。
因此,在回答中需要指出这一点,并建议遵守豆瓣的robots.txt,设置适当的请求间隔,避免频繁请求。
最后,保存数据到本地文件,示例中使用的是文本文件,每行写入标题和评分。
综上,整理后的回答应该包含上述代码,并解释各部分的作用,同时提醒注意事项和可能的限制。</think>以下是使用Python的requests库和标准库html.parser实现豆瓣电影Top250数据爬取的示例代码,包含反爬处理和数据保存功能:
```python
import requests
from html.parser import HTMLParser
class DoubanParser(HTMLParser):
def __init__(self):
super().__init__()
self.movies = []
self.current = {}
self.in_item = False
self.in_title = False
self.in_rating = False
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
if tag == 'div' and 'class' in attrs and 'item' in attrs['class'].split():
self.in_item = True
self.current = {}
elif self.in_item:
if tag == 'span' and attrs.get('class') == 'title':
self.in_title = True
elif tag == 'span' and attrs.get('class') == 'rating_num':
self.in_rating = True
def handle_data(self, data):
if self.in_title and 'title' not in self.current:
self.current['title'] = data.strip()
self.in_title = False
elif self.in_rating:
self.current['rating'] = data.strip()
self.in_rating = False
def handle_endtag(self, tag):
if tag == 'div' and self.in_item:
if self.current.get('title') and self.current.get('rating'):
self.movies.append(self.current)
self.in_item = False
# 配置请求头防止被拦截
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
try:
# 请求目标页面
response = requests.get('https://movie.douban.com/top250', headers=headers)
response.encoding = 'utf-8'
# 解析页面内容
parser = DoubanParser()
parser.feed(response.text)
# 保存数据到本地
with open('douban_movies.csv', 'w', encoding='utf-8') as f:
f.write('电影名称,评分\n')
for movie in parser.movies:
f.write(f'"{movie["title"]}",{movie["rating"]}\n')
print('数据已保存到douban_movies.csv')
except Exception as e:
print(f'发生错误: {str(e)}')
```
注意事项:
1. 需要遵守豆瓣的robots.txt规则,控制请求频率
2. 实际运行时可能需要处理验证码等反爬机制[^3]
3. 数据保存采用CSV格式,方便后续处理
4. 该代码仅处理静态HTML内容,动态加载数据需要额外处理[^3]
阅读全文
相关推荐


















