当前位置: 首页>后端>正文

Python3网络爬虫开发实战 第2版 崔庆才 著 人工智能 pdf python基础与网络爬虫设计

第2章 Python 基础

2.3 Python的数据结构和控制结构

元组和列表的区别:

列表生成以后还可以往里面继续添加数据,也可以从里面删除数据;但是元组一旦生成就不能修改。如果它里面只有整数、浮点数、字符串、另一个元组,就既不能添加数据,也不能删除数据,还不能修改里面数据的值。但是如果元组里面包含了一个列表,那么这个元组里面的列表依旧可以变化。

切片操作

格式为:

变量名[开始位置下标:结束位置下标:步长]

切片的结果包括“开始位置下标”所对应的元素,但是不包括“结束位置下标”所对应的元素。

列表末尾添加元素

Append,例如:

>>> list_4 = ['Python', ’爬虫’]
    >>> print(list_4)
    ['Python', ’爬虫’]
    >>> list_4.append(’一’)
    >>> print(list_4)
    ['Python', ’爬虫’, ’一’]
    >>> list_4.append(’酷’)
    >>> print(list_4)
    ['Python', ’爬虫’, ’一’, ’酷’]

元组和字符串不能添加新的内容,不能修改元组里面的非可变容器元素,也不能修改字符串里面的某一个字符。

通过Key来从字典中读取对应的Value,

有3种主要的格式:

变量名[key]
    变量名.get(key)
    变量名.get(key, ’在找不到key的情况下使用这个值’)

例如:

>>> example_dict = {'superman': ’超人是一个可以在天上飞的两足兽’, ’天才’: ’天才跑在时代的前面,把时代拖得气喘吁吁。',

    'xx': 0, 42: '42是一切的答案’}
    >>> print(example_dict[’天才’])
    天才跑在时代的前面,把时代拖得气喘吁吁。
    >>> print(example_dict.get(42))
    42是一切的答案
    >>> print(example_dict.get(’不存在的key'))
    None
    >>> print(example_dict.get(’不存在的key', ’找不到’))
    找不到

使用方括号的方式来读取字典的Value时,一定要保证字典里面有这个Key和它对应的Value,否则程序会报错。
使用get来读取,如果get只有一个参数,那么在找不到Key的情况下会得到“None”;如果get有两个参数,那么在找不到Key的情况下,会返回第2个参数。

修改已存在字典的Key对应Value/增加新的Key-Value对

变量名[key] = ’新的值’

如果Key不存在,就会创建新的Key-Value对;如果Key已经存在,就会修改它的原来的Value。例如:

>>> existed_dict = {'a': 123, 'b': 456}
    >>> print(existed_dict)
    {'b': 456, 'a': 123}
    >>> existed_dict['b'] = ’我修改了b'
    >>> print(existed_dict)
    {'b': ’我修改了b', 'a': 123}
    >>> existed_dict['new'] = ’我来也’
    >>> print(existed_dict)
    {'b': ’我修改了b', 'a': 123, 'new': ’我来也’}

需要特别注意的是,字典的Key的顺序是乱的,所以不能认为先添加到字典里面的数据就排在前面。

集合去重

集合最大的应用之一就是去重。
例如,把一个带有重复元素的列表先转换为集合,再转换回列表,那么重复元素就只会保留一个。 把列表转换为集合需要使用set()函数,把集合转换为列表使用list()函数:

duplicated_list = [3, 1, 3, 2, 4, 6, 6, 7, 's', 's', 'a']
unique_list = list(set(duplicated_list))
print(unique_list)
[1, 2, 3, 4, 's', 6, 7, 'a']

由于集合与字典一样,里面的值没有顺序,因此使用集合来去重是有代价的,代价就是原来列表的顺序也会被改变。

使用字典实现多重条件控制

如果有多个if,写起来会很烦琐,例如下面这一段代码:

if state == 'start':
      code = 1
    elif state == 'running':
      code = 2
    elif state == 'offline':
      code = 3
    elif state == 'unknown':
      code = 4
    else:
      code = 5

使用“if…elif…else…”会让代码显得冗长。如果使用字典改写,代码就会变得非常简洁:

state_dict = {'start': 1,'running': 2, 'offline': 3, 'unknown': 4}
    code = state_dict.get(state, 5)

for循环

for 循环从列表中取出每个元素

name_list=['a','b','c','d']
for name in name_list:
    print(name)

for循环从每个字符串里面获得一个字符

title="大家好,我叫EasyLake"
for character in title:
    print (character)

这里的每一个汉字、每一个字母、每一个标点符号都会被for循环分开读取。循环第1次得到的是“大”,第2次得到的是“家”,第3次得到的是“好”,以此类推。
在做爬虫的时候会遇到需要把列表展开的情况,常犯的一个错误就是把字符串错当成了列表展开。这个时候就会得到不正常的结果。

for循环把字典展开得到里面每一个key

menu_dict = {'红烧肉':'100元','水煮肉片':'50元','鸡汤':'1角'}
for key in menu_dict:
    print('菜品:{}'.format(key))
    print('价格:{}'.format(menu_dict[key]))
    print("---------------")

通过指定range里面的数字,可以控制循环的执行次数。需要特别注意的是,i是从0开始的。

在某些特殊的情况下,确实需要循环永远运行,这个时候需要这样写:

import time
    while True:
      你要执行的代码
      time.sleep(1)

如果要让循环永久运行,那么增加一个延迟时间是非常有必要的。time.sleep()的参数为一个数字,单位为秒。如果不增加这个延迟时间,就会导致循环超高速运行。在爬虫的开发过程中,如果超高速运行,很有可能导致爬虫被网站封锁。

第3章正则化表达式与文件操作

3.1 .1正则表达式的基本符号

1. 点号“.”:一个点号可以代替除了换行符以外的任何一个字符

一个点号可以代替除了换行符以外的任何一个字符,包括但不限于英文字母、数字、汉字、英文标点符号和中文标点符号。例如,有如下几个不同的字符串:

kingname
kinabcme
kin123me
kin我是谁me
kin嗨你好me
kin"m"me`

这些字符串的前3个字符都是“kin”,后两个字符都是“me”,只有中间的3个字符不同。如果使用点号来表示,那么全部都可以变成kin…me的形式,中间有多少个字就用多少个点。

2.星号“*”

只有一个星号 *:它前面的子表达式0次到无限次

一个星号表示它前面的一个子表达式(普通字符、另一个或几个正则表达式符号)0次到无限次。
例如,有如下几个不同的字符串:

如果快乐你就笑哈
    如果快乐你就笑哈哈
    如果快乐你就笑哈哈哈哈
    如果快乐你就笑哈哈哈哈哈哈哈哈哈

这些字符串里面,“哈”字重复出现,所以如果用星号来表示,那么就可以全部变成:

如果快乐你就笑哈*
如果快乐你就笑*

由于星号可以表示它前面的字符0次,所以即使写成“如果快乐你就笑”,没有“哈”字,也是满足这个正则表达式的。

一个点号+一个星号 .*

既然星号可以表示它前面的字符,那么如果它前面的字符是一个点号呢?例如下面这个正则表达式:

如.*哈

它表示在“如”和“哈”中间出现“任意多个除了换行符以外的任意字符”。这句话看起来有点绕,用下面几个字符串来说明,它们全部都可以用上面的这个正则表达式来表示:

如哈
    如果快乐哈
    如果快乐你就笑哈
    如果你知道1+1=2那么请计算地球的半径哈
    如aklsdjfjaf哈

3.问号“? ”

问号表示它前面的子表达式0次或者1次。注意,这里的问号是英文问号。
例如下面这两个不同的字符串:

笑起来。
 笑起来哈。

在汉字“来”和中文句号之间有0个或者1个“哈”字,都可以使用下面这个正则表达式来表示:

笑起来哈?。
点号+星号+问号 .*?

问号最大的用处是与点号和星号配合起来使用,构成“.*? ”。通过正则表达式来提取信息的时候,用到最多的也是这个组合。
下面的所有字符串:

如哈
    如果快乐哈
    如果快乐你就笑哈
    如果你知道1+1=2那么请计算地球的半径哈
    如aklsdjfjaf哈

都可以用下面这个正则表达式来表示:
如.*?哈`

.* 和.* ?d 的区别:
前者为贪婪模式,获取最长的满足条件的字符串;后者为非贪婪模式,获取最短的能满足条件的字符串。
前者最多只能匹配一个,后者可以匹配多个。

4.反斜杠/

反斜杠不仅可以把特殊符号变成普通符号,还可以把普通符号变成特殊符号。例如“n”只是一个普通的字母,但是“\n”代表换行符。

Python3网络爬虫开发实战 第2版 崔庆才 著 人工智能 pdf python基础与网络爬虫设计,Python3网络爬虫开发实战 第2版 崔庆才 著 人工智能 pdf python基础与网络爬虫设计_Python,第1张

5.数字“\d”:表示一位数字

正则表达式里面使用“\d”来表示一位数字。为什么要用字母d呢?因为d是英文“digital(数字)”的首字母。
再次强调一下,“\d”虽然是由反斜杠和字母d构成的,但是要把“\d”看成一个正则表达式符号整体。
如果要提取两个数字,可以使用\d\d;如果要提取3个数字,可以使用\d\d\d。但是如果不知道这个数有多少位怎么办呢?就需要用*号来表示一个任意位数的数字。
下面一段字符串:

是123455677,请记住它。
    是1,请记住它。
    是66666,请记住它。

全部都可以使用下面这个正则表达式来表示:

\d*:一个任意位数的数字。

6.小括号“()”:把括号里面的内容提取出来。

前面讲到的符号仅仅能让正则表达式“表示”一串字符串。但是如果要从一段字符串中“提取”出一部分的内容应该怎么办呢?这个时候就需要使用小括号了。
有如下一个字符串:
我的密码是:12345abcde你帮我记住。 可以看出,这里的密码左边有一个英文冒号,右边有一个汉字“你”。当构造一个正则表达式:.*?你时,得到的结果将会是:
:12345abcde你 然而,冒号和汉字“你”并不是密码的一部分,如果只想要“12345abcde”,就需要使用括号:
:(.*? )你 得到的结果就是:
12345abcde

3.1.2在Python中使用正则表达式

Python的正则表达式模块名字为“re”,也就是“regular expression”的首字母缩写。在Python中需要首先导入这个模块再进行使用。导入的语句为:
import re

1.findall:以列表的形式返回所有满足要求的字符串

Python的正则表达式模块包含一个findall方法,它能够以列表的形式返回所有满足要求的字符串。
findall的函数原型为:
re.findall(pattern, string, flags=0) pattern表示正则表达式,string表示原来的字符串,flags表示一些特殊功能的标志。
findall的结果是一个列表,包含了所有的匹配到的结果。如果没有匹配到结果,就会返回空列表。
当需要提取某些内容的时候,使用小括号将这些内容括起来,这样才不会得到不相干的信息。如果包含多个“(.? )”怎么返回呢?如图3-2所示,返回的仍然是一个列表,但是列表里面的元素变为了元组,元组里面的第1个元素是账号,第2个元素为密码。

图3-2 多个括号内的内容会以元组形式返回
请注意代码中的冒号和逗号,图3-1代码中为中文冒号和中文逗号;图3-2代码中为英文冒号和英文逗号。在实际使用正则表达式的过程中,中英文标点符号混淆常常会导致各种问题。特别是冒号、逗号和引号,虽然中英文看起来非常相似,但实际上中文冒号和英文冒号是不一样的,中文逗号和英文逗号也是不一样的。在某些字体里面,这种差异甚至无法察觉,因此在涉及正则表达式中的标点符号时,最好直接复制粘贴,而不要手动输入。
函数原型中有一个flags参数。这个参数是可以省略的。当不省略的时候,具有一些辅助功能,例如忽略大小写、忽略换行符等。这里以忽略换行符为例来进行说明,如图3-3所示。

图3-3 使用re.S作为flag来忽略换行符
在爬虫的开发过程中非常容易出现这样的情况,要匹配的内容存在换行符“\n”。要忽略换行符,就需要使用到“re.S”这个flag。虽然说匹配到的结果中出现了“\n”这个符号,不过总比什么都得不到强。内容里面的换行符在后期清洗数据的时候把它替换掉即可。

V3-6 search的使用
2.search
search()的用法和findall()的用法一样,但是search()只会返回第1个满足要求的字符串。一旦找到符合要求的内容,它就会停止查找。对于从超级大的文本里面只找第1个数据特别有用,可以大大提高程序的运行效率。
search()的函数原型为:

re.search(pattern, string, flags=0)
对于结果,如果匹配成功,则是一个正则表达式的对象;如果没有匹配到任何数据,就是None。如果需要得到匹配到的结果,则需要通过.group()这个方法来获取里面的值,如图3-4所示。

图3-4 使用.group()来获取search()方法找到的结果
只有在.group()里面的参数为1的时候,才会把正则表达式里面的括号中的结果打印出来。
group()的参数最大不能超过正则表达式里面括号的个数。参数为1表示读取第1个括号中的内容,参数为2表示读取第2个括号中的内容,以此类推,如图3-5所示。

图3-5 .group()的参数意义

V3-7 (.
)和(.? )的区别
3.“.
”和“.? ”的区别
在爬虫开发中,.
?这3个符号大多数情况下一起使用。
点号表示任意非换行符的字符,星号表示匹配它前面的字符0次或者任意多次。所以“.”表示匹配一串任意长度的字符串任意次。这个时候必须在“.”的前后加其他的符号来限定范围,否则得到的结果就是原来的整个字符串。
如果在“.”的后面加一个问号,变成“.? ”,那么可以得到什么样的结果呢?问号表示匹配它前面的符号0次或者1次。于是.?的意思就是匹配一个能满足要求的最短字符串。
这样说起来还是非常抽象,下面通过一个实际的例子来进行说明。请看下面这一段话:

我的微博密码是:1234567, QQ密码是:33445566, 银行卡密码是:888888, Github密码是:999abc999,帮我记住它们
这段话有一个显著的规律,即密码是:xxxxxx, ”,也就是在“密码是”这3个汉字的后面跟一个中文的冒号,冒号后面是密码,密码后面是中文的逗号。
如果想把这4个密码提取出来,可以构造以下两个正则表达式:

密码是:(.
),
密码是:(.? ),
配合Python的findall方法,得到结果如图3-6图所示。

图3-6 使用“.*”和“.*? ”返回的结果
使用“(.
)”得到的是只有一个元素的列表,里面是一个很长的字符串。
使用第2个正则表达式“(.? )”,得到的结果是包含4个元素的列表,每个元素直接对应原来文本中的每个密码。
举一个例子,10个人肩并肩并排站着,使用“(.
)”取到了第1个人左手到第10个人右手之间的所有东西,而使用“(.? )”取到的是“每个人”的左手和右手之间的东西。
一句话总结如下。
①“.
”:贪婪模式,获取最长的满足条件的字符串。
②“.? ”:非贪婪模式,获取最短的能满足条件的字符串。
3.1.3 正则表达式提取技巧
1.不需要compile
网上很多人的文章中,正则表达式使用re.compile()这个方法,导致代码变成下面这样:

import re
example_text = ’我是kingname, 我的微博账号是:kingname, 密码是:12345678, QQ账号是:99999, 密
码是:890abcd, 银行卡账号是:000001, 密码是:654321, Github账号是:99999@qq.com, 密码
是:7777love8888, 请记住他们。'
new_pattern=re.compile(’账号是:(.
? ), 密码是:(.*? ), ', re.S)
user_pass = re.findall(new_pattern, example_text)
print(user_pass)

V3-8 正则表达式提取技巧
这种写法虽然结果正确,但纯粹是画蛇添足,是对Python的正则表达式模块没有理解透彻的体现,是从其他啰嗦的编程语言中带来的坏习惯。如果阅读Python的正则表达式模块的源代码,就可以看出re.compile()是完全没有必要的。
对比re.compile()和re.findall()在源代码中的写法,如图3-7所示的两个方框。



https://www.xamrdz.com/backend/3pd1960877.html

相关文章: