方法1

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
import requests
from bs4 import BeautifulSoup
import os

url_template = "https://www.douban.com/tag/%E7%A4%BE%E5%8C%BA/article?start={}"

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'
}

# 存储HTML内容的列表
html_contents = []

# 爬取前三页内容
for i in range(0, 45, 15): # 每页显示15条,start参数每次增加15
response = requests.get(url_template.format(i), headers=headers)
response.raise_for_status() # 如果请求出错,抛出HTTPError异常
soup = BeautifulSoup(response.text, 'html.parser')
articles = soup.select('.article .item')
html_contents.append(response.text)

# 创建存储HTML文件的目录(如果不存在)
output_dir = 'douban_community_articles'
os.makedirs(output_dir, exist_ok=True)

# 保存前三页的HTML内容到文件中
for i, html in enumerate(html_contents):
with open(os.path.join(output_dir, f'page_{i+1}.html'), 'w', encoding='utf-8') as file:
file.write(html)

print(f"豆瓣社区图文前三页已保存至 {output_dir} 目录下。")

方法2

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

import requests
# 爬取网址
# http://bbs.itheima.com/forum-425-1.html
# http://bbs.itheima.com/forum-425-2.html
# http://bbs.itheima.com/forum-425-3.html
#https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p=0
def load_page(url):
'''
作用:根据url发送请求,获取服务器响应文件
url:需要爬取的url地址
'''
headers = {"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident / 5.0;"}
response = requests.get(url, headers=headers)
return response.text


def save_file(html, filename):
'''
作用:将html内容写入本地文件
html:服务器响应文件内容
'''
print("正在保存" + filename)
with open(filename, 'w', encoding='utf-8') as file:
file.write(html)


def heima_forum(begin_page, end_page):
'''
作用:黑马论坛爬虫调度器,负责组合处理每个页面的url
url:黑马论坛的url
begin_page:起始页码
end_page:结束页
'''
for page in range(begin_page, end_page + 1):
url = f'https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p={page}.html'
file_name = "第" + str(page) + "页.html"
html = load_page(url)
save_file(html, file_name)


if __name__ == "__main__":
begin_page = int(input("请输入起始页:"))
end_page = int(input("请输入结束页:"))
heima_forum(begin_page, end_page)

方法1语法解释

.format

在Python中,str.format() 方法用于格式化字符串。给定一个字符串和一个或多个值,该方法会返回一个新字符串,其中某些值(通过花括号 {} 指定)被替换为传递给 format() 方法的值。

在你提供的脚本中,url_template 是一个包含占位符 {} 的字符串,表示URL中的一个可变部分。这个占位符将被 format() 方法中的参数值替换。

1
url_template = "https://www.douban.com/tag/%E7%A4%BE%E5%8C%BA/article?start={}"

这里,{} 是将被替换的占位符。当你调用 url_template.format(i) 时,i 的值会被插入到字符串中的 {} 位置。

例如,如果 i 的值是 0,那么 url_template.format(0) 将返回:

1
https://www.douban.com/tag/%E7%A4%BE%E5%8C%BA/article?start=0

如果 i 的值是 10,那么 url_template.format(10) 将返回:

1
https://www.douban.com/tag/%E7%A4%BE%E5%8C%BA/article?start=10

在你的脚本中,这个 format() 方法被用在 for 循环中,每次循环时 i 的值都会改变(从 0 开始,每次增加 10,直到 20,但由于 range 的结束值是不包含的,所以实际上只会取到 0, 10, 20 这三个值),从而生成不同的URL来爬取豆瓣社区图文的不同页面。

疑问:为什么后缀是text保存的为什么却是html文件

首先,.text 并不是指文件的后缀名,而是指 Response 对象的一个属性名。这个属性包含了HTTP响应体的文本内容,它是以字符串形式呈现的,并且这个字符串的内容通常是HTML(对于网页请求来说)。但是,.text 属性本身并不决定保存文件时的后缀名。

当您使用 response.text 获取到HTML内容后,如果您想将其保存为文件,您可以自行选择文件的后缀名。通常,对于HTML内容,我们会选择 .html.htm 作为后缀名,因为这样可以更清晰地表明文件的类型和内容。

例如,您可以使用以下Python代码将 response.text 保存为一个HTML文件:

1
2
with open('example.html', 'w', encoding='utf-8') as file:
file.write(response.text)

在这个例子中,'example.html' 是您选择的文件名和后缀名,它告诉操作系统和任何查看该文件的人,这是一个HTML文件。

所以,总结来说,.textResponse 对象的一个属性,用于获取响应体的文本内容;而文件的后缀名(如 .html)是您在保存文件时自行选择的,用于表示文件的类型和内容。这两者之间没有直接的关联或依赖关系。

enumerate函数

enumerate() 函数是Python内置的一个非常实用的函数,它通常用于在for循环中获取每个元素的索引及其对应的值。这个函数会将一个可遍历的数据对象(例如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,即它会将一个可遍历的数据对象转换成一个包含索引和元素值的枚举对象。

基本语法如下:

1
enumerate(iterable, start=0)
  • iterable:一个可遍历的数据对象,例如列表、元组或字符串。
  • start:索引的起始位置,默认为0。

返回值是一个枚举对象,该对象包含每个元素的索引和值,通常会在for循环中被解包。

下面是一个简单的示例,展示了如何使用enumerate()函数:

1
2
3
4
fruits = ['apple', 'banana', 'cherry']

for index, fruit in enumerate(fruits):
print(f"Index: {index}, Fruit: {fruit}")

输出将会是:

1
2
3
Index: 0, Fruit: apple
Index: 1, Fruit: banana
Index: 2, Fruit: cherry

在这个例子中,enumerate(fruits) 会生成一个枚举对象,该对象在每次迭代时都会返回一个包含当前索引(从0开始)和当前水果名称的元组。然后,我们通过for循环将这些元组解包到indexfruit变量中,并打印出来。

enumerate()函数在需要同时跟踪元素索引和元素值的情况下非常有用,例如,在处理列表、元组或字符串时,你可能需要知道每个元素的位置。

保存为html文件到一个具体目录

代码解读如下:

1
2
with open(os.path.join(output_dir, f'page_{i+1}.html'), 'w', encoding='utf-8') as file:
file.write(html)
  • output_dir:这是一个字符串变量,代表输出文件的目录路径。
  • os.path.join(output_dir, f'page_{i+1}.html'):此函数用于将output_dir目录与文件名(例如'page_1.html''page_2.html'等,具体取决于i的值)合并,从而生成文件的完整路径。f'page_{i+1}.html'是一个f-string(格式化字符串字面量),它允许您在字符串中直接嵌入Python表达式(此处为i+1)。
  • 'w':这是open()函数的模式参数,表示以写入模式打开文件。若文件已存在,则会被覆盖;若不存在,则会自动创建。
  • encoding='utf-8':此参数指定了文件的编码格式为UTF-8,这对于确保文件中包含非ASCII字符(如中文)时,能够正确读写至关重要。
  • as file:此部分将打开的文件对象赋给变量file,您可以通过该变量来操作文件(如写入数据)。
  • file.write(html):此行代码负责将变量html(包含HTML内容的字符串)写入之前打开的文件中。

由于使用了with语句,因此无需手动关闭文件。当with块执行完毕后,文件会自动关闭,这有助于避免文件泄露或损坏等潜在问题。

  • with 是Python中的一个上下文管理器,它允许代码块在执行前后,自动执行某些设置或清理工作。