Python3.X网络爬虫学习(二)

一、HTTP协议请求实战

如果要进行客户端与服务器端之间的消息传递,我们可以使用HTTP协议请求进行。

  1. GET请求:GET请求会通过URL网址传递信息,可以直接在URL中写上要传递的信息,也可以由表单进行传递。如果使用表单进行传递,表单中的信息会自动转化为URL地址中的数据,通过URL地址传递。
  2. POST请求:可以向服务器提交数据,是一种比较主流也比较安全的数据传递方式,比如在登录时,经常使用POST请求发送数据。

GET请求实例分析:

比如在百度上查询一个关键词,输入“love”,会观察到URL的变化:https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=love&rsv_pq=8d9d300800006e7e&rsv_t=9897ZRevPAHfRbSBtLI4jAbDCKE6aHajYVciCVNunovZTBN4urnIbdq5%2FKs&rqlang=cn&rsv_enter=1&rsv_sug3=5&rsv_sug1=4&rsv_sug7=101&rsv_sug2=0&inputT=1235&rsv_sug4=1485&rsv_sug=1

可以发现,查询信息是通过URL传递的,这里就是采用的HTTP请求中的GET方法。我们将网址提取出来分析:ie=utf-8代表编码信息,而字段wd=love就是我们要查询的信息,假如我们简化一下该网址:https://www.baidu.com/s?wd=love,此时只包含对应的wd字段,将该网址复制到浏览器刷新,同样也可以得到关键词为love的搜索结果。

根据这个规律,我们就可以通过构造GET请求,用爬虫实现在百度上自动查询某个关键词。

import urllib.request
keyword = "love"
#这里要注意,去掉https后面的s
url = "http://www.baidu.com/s?wd=" + keyword
req = urllib.request.Request(url)
data = urllib.request.urlopen(req).read()
#将爬取到的网页内容存储到1.html文件中
file = open("E://1.html","wb")
file.write(data)
file.close()

代码分析:

首先将查询的关键词赋给keyword变量,然后按照之前分析的URL格式,构造出对应的url变量,再通过urllib.request.Request()构建一个Request对象,并赋给req,通过urlopen打开对应的Request对象。此时网址中包含了GET请求信息,所以会以GET请求的方式获取该页面,随后读取页面的内容赋给data,并写入1.html文件中。

注:若关键词为中文,则应在构造url之前,先将关键词部分进行编码,编码之后再构造对应的url:

real_key = urllib.request.quote(keyword)
url = "http://www.baidu.com/s?wd=" + real_key

POST请求实例分析:

我们在进行注册、登录等操作的时候,基本上都会遇到POST请求。由于登录要用到Cookie的知识,所以关于如何登录这一块的内容,放在下次讲解Cookie时详细介绍,这里了解一下爬虫怎样实现POST数据的传递:

  1. 设置好URL地址
  2. 构建表单数据,并使用urllib.parse.urlencode对数据进行编码
  3. 创建Request对象,参数包括URL地址和要传递的数据
  4. 使用add_header()添加头信息,模拟浏览器进行爬取
  5. 使用urllib.request.urlopen()打开对应的Request对象,完成信息的传递
  6. 后续处理,比如读取网页内容、将内容写入文件等

具体代码到学习Cookie时一起讲解。


二、代理服务器的设置

有时用同一个IP去爬取一个网站上的网页,久了之后会被该网站服务器屏蔽。如果我们爬取别人网站的时候,在对方服务器上显示的是别人的IP地址,即使对方将显示出来的IP地址屏蔽了也无关紧要,因为我们可以换另一个IP地址继续爬取。

使用代理服务器就能很好地解决这个问题。我们可以在互联网上搜索对应的代理服务器地址,比如:http://www.xicidaili.com/:

Python3.X网络爬虫学习(二)_第1张图片

可以看到,这里会更新很多代理IP地址,我们尽量找验证时间短的,成功的概率会更大。此时我们选择第一个代理IP地址:183.154.52.56,端口号是45186,完整的格式为:网址:端口号。即,183.154.52.56:45186。

用了代理IP地址后,我们就可以进行相应程序的编写了:

import urllib.request
def use_proxy(proxy_addr,url):
    proxy = urllib.request.ProxyHandler({"http":proxy_addr})
    opener = urllib.request.build_opener(proxy,urllib.request.HTTPHandler)
    urllib.request.install_opener(opener)
    data = urllib.request.urlopen(url).read().decode("utf-8")
    return data
proxy_addr = "183.154.52.56:45186"
data = use_proxy(proxy_addr,"http://www.baidu.com")
print(len(data))

首先定义了一个函数,该函数用于实现代理服务器来爬取某个页面。函数中有两个形参,第一个形参为代理服务器的地址,第二个形参代表要爬取的网页的地址。

然后使用ProxyHandler来设置服务器信息,格式为:{"http":代理服务器地址},接着创建一个opener对象,第一个参数为代理信息,第二个参数为urllib.request.HTTPHandle类。

一般情况下我们访问一个URL需要使用一个opener,即urlopen(),但基本的urlopen()函数不支持验证、Cookie或其他HTTP高级功能。要支持这些功能,必须使用build_opener()函数来创建自己的自定义open对象。

为了方便,可以使用urllib.request.install_opener()创建全局默认的opener对象,那么在使用urlopen()时也会使用我们安装的opener对象,所以下面才可以直接使用urllib.request.urlopen()打开对应网址爬取网页并读取。

程序执行的结果如下:

Python3.X网络爬虫学习(二)_第2张图片


三、正则表达式

什么是正则表达式

简单来说,正则表达式就是描述字符串排列的一套规则,主要用于字符串的匹配,在Python中,我们一般会使用re模块来实现正则表达式的功能。

接下来介绍一下正则表达式的基础知识:

1.原子

原子是正则表达式中最基本的组成单位,常见的原子有以下几类:

  1. 普通字符作为原子
  2. 非打印字符作为原子
  3. 通用字符作为原子
  4. 原子表

接下来分别讲解:

(1)普通字符作为原子

import re
pattern = "lmn"
string = "http://ilovelmn.com"
result = re.search(pattern,string)
print(result)

其中,pattern为普通字符组成的字符串,将其看作正则表达式,然后使用re模块中的search()函数,从string中去匹配对应的正则表达式,此时执行结果如下,可以看到成功匹配到了对应的"lmn",因为在string中包含对应符合正则表达式规则的字符串。

(2)非打印字符作为原子

非打印字符指的是一些在字符串中用于格式控制的符号,比如换行符等。例如可以通过以下程序来实现对换行符的匹配。

import re
pattern = "\n"
string = '''http://ilovelmn.com
iloveyou'''
result = re.search(pattern,string)
print(result)

若无法成功匹配,则输出None。

(3)通用字符作为原子

通用字符,即一个原子可以匹配一类字符。

\w:匹配任意一个字母、数字或下划线。

\W:匹配除字母、数字和下划线以外的任意一个字符。

\d:匹配任意一个十进制数。

\D:匹配除十进制数以外的任意一个其它字符。

\s:匹配任意一个空白字符。

\S匹配除空白字符以外的任意一个其它字符。

import re
pattern = "\w\dloveyou\w"
string = "http://ilovelmn86loveyou6com"
result = re.search(pattern,string)
print(result)

程序结果显示匹配成功。

(4)原子表

使用原子表,可以定义一组地位平等的原子,然后匹配的时候会取该原子表中的任意一个原子进行匹配,比如[xyz]就是一个原子表,这个院子表中定义了3个原子,这3个原子地位平等。类似的,[^]代表的是除了中括号里面的原子均可以匹配。程序略。

2.元字符

元字符就是正则表达式中具有一些特殊含义的字符,比如重复n次前面的字符等。

.:匹配除换行符以外的任意字符。

^:匹配字符串的开始位置。

$:匹配字符串的结束位置。

*:匹配0次、1次或多次前面的原子。

?:匹配0次或1次前面的原子。

+:匹配1次或多次前面的原子。

{n}:前面的原子恰好出现n次。

{n,}:前面的原子至少出现n次。只要满足格式,就会在源字符中尽可能多地匹配字符。

{n,m}:前面的原子至少出现n次,至多出现m次。

|:模式选择符。

():模式单元符。

具体来说,以上元字符可以分为:任意匹配元字符、边界限制元字符、限定符、模式选择符合模式单元等。

3.模式修正

模式修正符,可以在不改变正则表达式的情况下,通过模式修正符改变正则表达式的含义,从而实现一些匹配结果的调整。

I:匹配时忽略大小写。

M:多行匹配。

L:做本地化识别匹配。

U:根据Unicode字符及解析字符。

S:让 . 去匹配包括换行符,即用了该模式修正后," . "匹配就可以匹配任意字符了。

import re
pattern = "love"
string = "ILoveYou"
result = re.search(pattern,string,re.I)
print(result)

贪婪模式与懒惰模式

总的来说,贪婪模式的核心点就是尽可能多地匹配,而懒惰模式的核心点就是尽可能少地匹配。

通常情况下,我们想在某些字符间匹配任意字符,像" p.*y "这样写默认是使用贪婪模式的,如果要转化为懒惰模式,需要在对应的" .* "后面加上" ? ",则转化为懒惰模式。

正则表达式的常见函数

1.re.match()函数

如果想要从源字符串的起始位置匹配一个模式,我们可以使用re.match()函数,该函数的使用格式为:re.match(pattern,string,flag),第一个参数代表对应的正则表达式,第二个参数代表对应的源字符,第三个参数是可选参数,可以放模式修正符等信息。

import re
pattern = ".love"
string = "iloveyoupython"
result = re.match(pattern,string).span()
print(result)

其中的span()可以过滤掉一些信息,只留下匹配成功的结果在源字符串中的位置。

2.re.search()函数

使用该函数进行匹配,会扫描整个字符串并进行相应的匹配。该函数与re.match()函数最大的不同是,后者从源字符串的开头进行匹配,而前者会在全文中进行检索并匹配。

3.全局匹配函数

如果源字符串中有多个结果符合模式,我们如何将符合模式的内容全部都匹配出来呢?

思路如下:

  1. 使用re.compile()对正则表达式进行预编译。
  2. 编译后,使用findall()根据正则表达式从源字符串中将全部匹配的结果找出。
import re
pattern = re.compile(".love.")
string = "iloveyouthislovedabendan"
result = pattern.findall(string)
print(result)

4.re.sub()函数

如果想根据正则表达式来实现替换某些字符串的功能,可以使用re.sub()函数实现,格式:re.sub(pattern,rep,string,max)。第一个参数为对应的正则表达式,第二个参数为替换成的字符串,第三个参数为源字符串,第四个参数为可选项,代表最多替换的次数,如果忽略不写,则会将符合模式的结果全部替换。

import re
pattern = "python."
string = "iloveyouthispythonlovepythondabendan"
#全部替换
result1 = re.sub(pattern,"HZC",string)
#最多替换一次
result2 = re.sub(pattern,"HZC",string,1)
print(result1)
print(result2)

 

你可能感兴趣的:(Python,基础)