今天一共做了 3 个小项目。

项目 1:豆瓣前 top250 电影爬取

目标是爬取前 250 (其实没有那么多)电影的名称、评分等信息。

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
import requests
import re
import csv

url = "https://movie.douban.com/top250";
header = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
resp = requests.get(url, headers=header);

# print(resp.text);
# with open("tmp.html", "w", encoding="utf-8") as f:
# f.write(resp.text);

page_content = resp.text;

obj = re.compile(r'<li>.*?<div class="item">.*?<span class="title">(?P<name>.*?)</span>'
r'.*?<p class="">.*?<br>\s*(?P<year>.*?)&nbsp'
r'.*?<span class="rating_num" property="v:average">(?P<rate>.*?)</span>\s*<span property="v:best" content="10.0"></span>'
r'.*?<span>(?P<people>.*?)人评价</span>.*?</li>', re.S);

result = obj.finditer(page_content);

f = open("data.csv", mode= "w", encoding="utf-8", newline = "");
# 这里添加参数 newline 可以有效防止空行问题。
csvwriter = csv.writer(f);
# csv.writer 中可以存入一个文件对象。返回一个 csv 文件操作对象。

for it in result:
# print(it.group("name"));
# # print(it.group("year"));
# print(it.group("year").strip());
# print(it.group("rate"));
# print(it.group("people"));
dic = it.groupdict();
dic['year'] = dic['year'].strip();
csvwriter.writerow(dic.values());
# dic.values() 以列表返回字典中的所有值。

# strip 方法,属于 str 类,可以去除前缀和后缀的空白字符。
f.close();
resp.close();

print("over!");
  • 要点 1:requests.get() 方法返回 Response 类,其中的两个数据:textcontent 都用来储存请求返回的数据,不同的是,前者以文本形式保存,也就是经过了编码。其中,编码形式可以通过改变 Response 类中的 encoding 成员(str 类型)来进行设定,默认是通过 unicode 进行编码。后者是以字节的形式返回数据,这在返回数据并非文本时(例如照片,视频等)非常有用。

  • 要点 2:python 中 str 的拼接可以直接通过罗列 ‘…’ 来进行,两个不同的被单引号框住的字符串直接的空格和回车会被自动忽略。

  • 要点 3:r 字符串表示原始字符串,转义字符(例如 \n,\r)将被保留而不进行转义。

  • 要点 4:requests.get() 方法可以接受 headers 参数,后者本质上是一个字典,可以用来更改请求页面时使用的请求头信息。
    实际上还有一个参数 verify,默认值是 True,可以设定为 False 以爬取那些并没有 ca 安全认证的网页。

  • 要点 5:Match 类型中的 groupdict 方法可以把所有组中设定的 keyvalue 组成字典,而不用像注释那样一个一个进行。

  • 要点 6:open 函数返回一个文件操作对象 TextIOWrapper,而 csv.writer 方法可以接受一个文件操作对象,返回一个 csv 文件操作对象。其中的 writerow 方法接受一行列表(作为参数),并可以往对应的文件里面写入一行数据(以 csv 文件的格式,即用“,”隔开),

  • 要点 7:open 函数中的 newline 参数可以很好的解决 csv 文件读写时可能出现的空白行问题,这里做出以下解释:

首先,应该知道的是在不同的操作系统下,换行符不同。例如,在 windows 系统下,\r\n 作为一个字符表示换行,而在类 Unix 系统中,\n 表示换行。

python 文档是这样来解释 newline 的作用的:

On input,if newline is None, universal newlines mode is enabled. Lines in the input can end in '\n', '\r', or '\r\n', and these are translated into '\n' before being returned to the caller. If it is '', universal newline mode is enabled, but line endings are returned to the caller untranslated. If it has any of the other legal values, input lines are only terminated by the given string, and the line ending is returned to the caller untranslated. On output,if newline is None, any '\n' characters written are translated to the system default line separator,os.linesep. If newline is '', no translation takes place. If new line is any of the other legal values, any '\n' characters written are translated to the given string

而 ChatGPT 给出的中文解释如下:

输入(或读取)时:

newline=None 时:启用了“通用换行模式”(universal newlines mode)。文件中的换行符可以是 \n、\r\r\n,但在返回给调用者(读取内容)之前,所有这些换行符都会被统一转换为 \n。newline=''(空字符串)时:同样启用了“通用换行模式”,但不同的是,不会对文件中的换行符进行转换,直接原样返回给调用者。newline 是其他合法值时:输入行(也就是读取的内容)只会在遇到这个指定字符串时被认为是“行结束”(only terminated 的意思)。换句话说,这种情况下,行必须以你指定的字符串结束,否则不认为是“换行”。换行符原样返回,不进行转换。

输出(或写入):

newline=None 时:输出文件中,每当写入 \n 时,会被转换为系统默认的换行符(通常是 \n\r\n,由 os.linesep 决定)。newline=''(空字符串)时:不会对 \n 进行任何转换,直接原样写入。newline 是其他合法值时(比如一个特定的字符串):所有的 \n 都会被转换为你指定的字符串。

对上述文字这里不再做任何更加详细的解释,目前足以解释当前的问题。不加 newline="" 时,csvwriter.writerow 会自动在字符串结尾加入换行符,笔者电脑系统为 windows 系统,于是会在末尾加上 \r\n,而在写入时 \n 变为 \r\n,于是字符串末尾会变成 \r\r\n,后者在 windows 环境下会被解释成换两次行,所以会出现空白行的存在。加上该参数后,将不会出现此问题。

项目 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
import requests
import re

domain = "https://www.dytt89.com/";
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
resp = requests.get(domain, verify=False, headers=headers); # 去掉 ca 安全验证。 verify = false。原因是有的网站可能并没有购买 ca 证书。
resp.encoding = 'gb2312'; # 指定字符集。
# print(resp.text);

obj1 = re.compile(r"2025必看热片.*?<ul>(?P<ul>.*?)</ul>", re.S);
result1 = obj1.finditer(resp.text);

obj2 = re.compile(r"<li><a href='(?P<href>.*?)'", re.S);
child_href_list = [];

obj3 = re.compile(r'◎片  名\s+(?P<movie>.*?)<br />.*?<td style="WORD-WRAP: break-word" bgcolor="#fdfddf"><a href="(?P<down>.*?)">', re.S);
for it in result1:
# print(it.group("ul"));
ul = it.group("ul");

result2 = obj2.finditer(ul);
for itt in result2:
child_href = domain + itt.group('href').strip("/")
# print(itt.group("href"));
child_href_list.append(child_href);

for href in child_href_list:
child_resp = requests.get(href, verify=False, headers=headers);
child_resp.encoding = 'gb2312';
# print(child_resp.text);
result3 = obj3.search(child_resp.text);
print(result3.group("movie"));
print(result3.group("down"));
# break;
  • 要点 1:strstrip() 方法可以去掉前缀后缀字符,如果没有参数则去掉空白字符,有参数(str,代表一个字符集)则去掉前导或后导的字符集中的字符。

项目 3:爬取图片

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

url = "https://www.umeituku.com/bizhitupian/weimeibizhi/";
resp = requests.get(url);
resp.encoding = "utf-8";
# print(resp.text);

bs = BeautifulSoup(resp.text, "html.parser");
childbs = bs.find_all("a", attrs={"class": "TypeBigPics"});

urllist = [];

for cbs in childbs:
# urllist.append(cbs.get('href'));
href = cbs.get('href');
print(href);
resp2 = requests.get(href);
resp2.encoding = "utf-8";
bs2 = BeautifulSoup(resp2.text, "html.parser");
p = (bs2.find_all("p",attrs={"align": "center"}) + bs2.find_all("p", attrs={"style": "text-align:center;"}))[-1];
img = p.find("img");
print(img);
# print(img.get("src"));
src = img.get("src");
img_resp = requests.get(src);
img_name = src.split("/")[-1];
with open("img/"+img_name, mode="wb") as f:
# 这里,w 仍然意味着写入,而 b 表示以二进制方式写入。
f.write(img_resp.content);
print("over!", img_name);
time.sleep(1);
# break;


# print(urllist);
  • 要点 1:BeautifulSoup 构造函数接受需要解析的 html 文本,并且需要指明需要解析的就是 html 文本 (html.parser)。返回 Tag 类型,Tag 类型有 find()find_all() 方法,可以传入两个参数:html 标签和 html 属性,用来寻找 Tag 类型对应的 html 文本符合条件的 html 标签及其子标签。find() 方法只返回能找到的第一个结果,返回值为 Tag 类型。而 find_all() 方法返回所有能找到的结果,返回一个列表,列表中的元素为 Tag 类型。参数 html 属性并非必须。Tag 类型有 get() 方法,参数为标签属性名称,返回的是属性具体的值。同时,Tag 标签的 text 数据返回标签以及所有子标签中的所有文本信息。今天一共做了 3 个小项目。

项目 1:豆瓣前 top250 电影爬取

目标是爬取前 250 (其实没有那么多)电影的名称、评分等信息。

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
import requests
import re
import csv

url = "https://movie.douban.com/top250";
header = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
resp = requests.get(url, headers=header);

# print(resp.text);
# with open("tmp.html", "w", encoding="utf-8") as f:
# f.write(resp.text);

page_content = resp.text;

obj = re.compile(r'<li>.*?<div class="item">.*?<span class="title">(?P<name>.*?)</span>'
r'.*?<p class="">.*?<br>\s*(?P<year>.*?)&nbsp'
r'.*?<span class="rating_num" property="v:average">(?P<rate>.*?)</span>\s*<span property="v:best" content="10.0"></span>'
r'.*?<span>(?P<people>.*?)人评价</span>.*?</li>', re.S);

result = obj.finditer(page_content);

f = open("data.csv", mode= "w", encoding="utf-8", newline = "");
# 这里添加参数 newline 可以有效防止空行问题。
csvwriter = csv.writer(f);
# csv.writer 中可以存入一个文件对象。返回一个 csv 文件操作对象。

for it in result:
# print(it.group("name"));
# # print(it.group("year"));
# print(it.group("year").strip());
# print(it.group("rate"));
# print(it.group("people"));
dic = it.groupdict();
dic['year'] = dic['year'].strip();
csvwriter.writerow(dic.values());
# dic.values() 以列表返回字典中的所有值。

# strip 方法,属于 str 类,可以去除前缀和后缀的空白字符。
f.close();
resp.close();

print("over!");
  • 要点 1:requests.get() 方法返回 Response 类,其中的两个数据:textcontent 都用来储存请求返回的数据,不同的是,前者以文本形式保存,也就是经过了编码。其中,编码形式可以通过改变 Response 类中的 encoding 成员(str 类型)来进行设定,默认是通过 unicode 进行编码。后者是以字节的形式返回数据,这在返回数据并非文本时(例如照片,视频等)非常有用。

  • 要点 2:python 中 str 的拼接可以直接通过罗列 ‘…’ 来进行,两个不同的被单引号框住的字符串直接的空格和回车会被自动忽略。

  • 要点 3:r 字符串表示原始字符串,转义字符(例如 \n,\r)将被保留而不进行转义。

  • 要点 4:requests.get() 方法可以接受 headers 参数,后者本质上是一个字典,可以用来更改请求页面时使用的请求头信息。
    实际上还有一个参数 verify,默认值是 True,可以设定为 False 以爬取那些并没有 ca 安全认证的网页。

  • 要点 5:Match 类型中的 groupdict 方法可以把所有组中设定的 keyvalue 组成字典,而不用像注释那样一个一个进行。

  • 要点 6:open 函数返回一个文件操作对象 TextIOWrapper,而 csv.writer 方法可以接受一个文件操作对象,返回一个 csv 文件操作对象。其中的 writerow 方法接受一行列表(作为参数),并可以往对应的文件里面写入一行数据(以 csv 文件的格式,即用“,”隔开),

  • 要点 7:open 函数中的 newline 参数可以很好的解决 csv 文件读写时可能出现的空白行问题,这里做出以下解释:

首先,应该知道的是在不同的操作系统下,换行符不同。例如,在 windows 系统下,\r\n 作为一个字符表示换行,而在类 Unix 系统中,\n 表示换行。

python 文档是这样来解释 newline 的作用的:

On input,if newline is None, universal newlines mode is enabled. Lines in the input can end in '\n', '\r', or '\r\n', and these are translated into '\n' before being returned to the caller. If it is '', universal newline mode is enabled, but line endings are returned to the caller untranslated. If it has any of the other legal values, input lines are only terminated by the given string, and the line ending is returned to the caller untranslated. On output,if newline is None, any '\n' characters written are translated to the system default line separator,os.linesep. If newline is '', no translation takes place. If new line is any of the other legal values, any '\n' characters written are translated to the given string

而 ChatGPT 给出的中文解释如下:

输入(或读取)时:

newline=None 时:启用了“通用换行模式”(universal newlines mode)。文件中的换行符可以是 \n、\r\r\n,但在返回给调用者(读取内容)之前,所有这些换行符都会被统一转换为 \n。newline=''(空字符串)时:同样启用了“通用换行模式”,但不同的是,不会对文件中的换行符进行转换,直接原样返回给调用者。newline 是其他合法值时:输入行(也就是读取的内容)只会在遇到这个指定字符串时被认为是“行结束”(only terminated 的意思)。换句话说,这种情况下,行必须以你指定的字符串结束,否则不认为是“换行”。换行符原样返回,不进行转换。

输出(或写入):

newline=None 时:输出文件中,每当写入 \n 时,会被转换为系统默认的换行符(通常是 \n\r\n,由 os.linesep 决定)。newline=''(空字符串)时:不会对 \n 进行任何转换,直接原样写入。newline 是其他合法值时(比如一个特定的字符串):所有的 \n 都会被转换为你指定的字符串。

对上述文字这里不再做任何更加详细的解释,目前足以解释当前的问题。不加 newline="" 时,csvwriter.writerow 会自动在字符串结尾加入换行符,笔者电脑系统为 windows 系统,于是会在末尾加上 \r\n,而在写入时 \n 变为 \r\n,于是字符串末尾会变成 \r\r\n,后者在 windows 环境下会被解释成换两次行,所以会出现空白行的存在。加上该参数后,将不会出现此问题。

项目 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
import requests
import re

domain = "https://www.dytt89.com/";
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
resp = requests.get(domain, verify=False, headers=headers); # 去掉 ca 安全验证。 verify = false。原因是有的网站可能并没有购买 ca 证书。
resp.encoding = 'gb2312'; # 指定字符集。
# print(resp.text);

obj1 = re.compile(r"2025必看热片.*?<ul>(?P<ul>.*?)</ul>", re.S);
result1 = obj1.finditer(resp.text);

obj2 = re.compile(r"<li><a href='(?P<href>.*?)'", re.S);
child_href_list = [];

obj3 = re.compile(r'◎片  名\s+(?P<movie>.*?)<br />.*?<td style="WORD-WRAP: break-word" bgcolor="#fdfddf"><a href="(?P<down>.*?)">', re.S);
for it in result1:
# print(it.group("ul"));
ul = it.group("ul");

result2 = obj2.finditer(ul);
for itt in result2:
child_href = domain + itt.group('href').strip("/")
# print(itt.group("href"));
child_href_list.append(child_href);

for href in child_href_list:
child_resp = requests.get(href, verify=False, headers=headers);
child_resp.encoding = 'gb2312';
# print(child_resp.text);
result3 = obj3.search(child_resp.text);
print(result3.group("movie"));
print(result3.group("down"));
# break;
  • 要点 1:strstrip() 方法可以去掉前缀后缀字符,如果没有参数则去掉空白字符,有参数(str,代表一个字符集)则去掉前导或后导的字符集中的字符。

项目 3:爬取图片

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

url = "https://www.umeituku.com/bizhitupian/weimeibizhi/";
resp = requests.get(url);
resp.encoding = "utf-8";
# print(resp.text);

bs = BeautifulSoup(resp.text, "html.parser");
childbs = bs.find_all("a", attrs={"class": "TypeBigPics"});

urllist = [];

for cbs in childbs:
# urllist.append(cbs.get('href'));
href = cbs.get('href');
print(href);
resp2 = requests.get(href);
resp2.encoding = "utf-8";
bs2 = BeautifulSoup(resp2.text, "html.parser");
p = (bs2.find_all("p",attrs={"align": "center"}) + bs2.find_all("p", attrs={"style": "text-align:center;"}))[-1];
img = p.find("img");
print(img);
# print(img.get("src"));
src = img.get("src");
img_resp = requests.get(src);
img_name = src.split("/")[-1];
with open("img/"+img_name, mode="wb") as f:
# 这里,w 仍然意味着写入,而 b 表示以二进制方式写入。
f.write(img_resp.content);
print("over!", img_name);
time.sleep(1);
# break;


# print(urllist);
  • 要点 1:BeautifulSoup 构造函数接受需要解析的 html 文本,并且需要指明需要解析的就是 html 文本 (html.parser)。返回 Tag 类型,Tag 类型有 find()find_all() 方法,可以传入两个参数:html 标签和 html 属性,用来寻找 Tag 类型对应的 html 文本符合条件的 html 标签及其子标签。find() 方法只返回能找到的第一个结果,返回值为 Tag 类型。而 find_all() 方法返回所有能找到的结果,返回一个列表,列表中的元素为 Tag 类型。参数 html 属性并非必须。Tag 类型有 get() 方法,参数为标签属性名称,返回的是属性具体的值。同时,Tag 标签的 text 数据返回标签以及所有子标签中的所有文本信息。