简单的Python3爬虫

我们先从分析原理入手,然后再使用Python提供的基本的库urllib。

注意,我全程使用的是Python3,如果你必须使用不同版本,请自行百度某些库及函数的转换,需要使用的库不一定你的电脑上预装了,所以请自行百度安装。

原理

网络爬虫,也叫网络蜘蛛(Web Spider),如果把互联网比喻成一个蜘蛛网,Spider就是一只在网上爬来爬去的蜘蛛。网络爬虫就是根据网页的地址来寻找网页的,也就是URL。

URL

URL就是统一资源定位符(Uniform Resource Locator),它的一般格式如下(带方括号[]的为可选项):

protocol ://hostname[:port]/path/[;parameters][?query]#fragment

可见,一个URL包含三个部分:

  1. protocol:协议,例如https,http等;
  2. hostname[:port]:主机名(端口号为可选参数),一般网站默认的端口号为80,例如我的博客域名www.meng.uno,可以作为主机名使用;
  3. path:第三部分就是主机资源的具体地址,如目录和文件名等。

爬虫就是向URL发送请求,然后得到响应,基本就实现了爬取网页的功能。

URI可以分为URL,URN或同时具备locators 和names特性的一个东西。URN作用就好像一个人的名字,URL就像一个人的地址。换句话说:URN确定了东西的身份,URL提供了找到它的方式。

从浏览器发送和接收数据看起

进入我的首页www.meng.uno,打开浏览器的“检查”功能,选项卡选到“Network”,然后点击所有文章,随便选择一条,我们可以发现如下截图的"Headers"

Headers

我们可以发现最明显的有两个区域(我已经圈出来了):“request”和“response”。从字面意思上来看,我们就知道分别是(发送的)请求和(收到的)回复。

接收的信息是我们请求的网页给的,不用我们管,但是“请求的网页”是我们需要提前设定的,当然最简单的方式就是什么都不设置。爬虫会增加网站的负荷,所以很多网站希望大家通过API的方式使用其开放的资源而禁止爬虫,其中的一个做法就是判断你的请求内容(不全的基本都是爬虫)。于是,为了做到一个完整的可用的爬虫,我们需要模拟真实用户的请求,这就要求我们伪造“User Agent”。

常见的“User Agent”列举如下:

  1. Android

    • Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19
    • Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
    • Mozilla/5.0 (Linux; U; Android 2.2; en-gb; GT-P1000 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
  2. Firefox

    • Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0
    • Mozilla/5.0 (Android; Mobile; rv:14.0) Gecko/14.0 Firefox/14.0
  3. Google Chrome

    • Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36
    • Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19
  4. iOS

    • Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3
    • Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A101a Safari/419.3

User Agent已经设置好了,但是还应该考虑一个问题,程序的运行速度是很快的,如果我们利用一个爬虫程序在网站爬取东西,一个固定IP的访问频率就会很高,这不符合人为操作的标准,因为人操作不可能在几ms内,进行如此频繁的访问。所以一些网站会设置一个IP访问频率的阈值,如果一个IP访问频率超过这个阈值,说明这个不是人在访问,而是一个爬虫程序。

一个很简单的解决办法就是设置延时,但是这显然不符合爬虫快速爬取信息的目的,所以另一种更好的方法就是使用IP代理。使用代理的步骤:

  • 调用urlib.request.ProxyHandler(),proxies参数为一个字典;
  • 创建Opener(类似于urlopen,这个代开方式是我们自己定制的);
  • 安装Opener;

这个网站提供了很多代理主机:http://www.xicidaili.com/

正则表达式

我直接以表格的形式呈现好了:

元字符 说明
. 代表任意字符
[ ] 匹配内部的任一字符或子表达式
[^] 对字符集和取非
- 定义一个区间
\ 对下一字符取非(通常是普通变特殊,特殊变普通)
* 匹配前面的字符或者子表达式0次或多次
*? 惰性匹配上一个
+ 匹配前一个字符或子表达式一次或多次
+? 惰性匹配上一个
? 匹配前一个字符或子表达式0次或1次重复
{n} 匹配前一个字符或子表达式
{m,n} 匹配前一个字符或子表达式至少m次至多n次
{n,} 匹配前一个字符或者子表达式至少n次
{n,}? 前一个的惰性匹配
^ 匹配字符串的开头
\A 匹配字符串开头
$ 匹配字符串结束
[\b] 退格字符
\c 匹配一个控制字符
\d 匹配任意数字
\D 匹配数字以外的字符
\t 匹配制表符
\w 匹配任意数字字母下划线
\W 不匹配数字字母下划线

代码

简单带错误信息的获取网页内所有URL的爬虫

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
#获取URL的包
import urllib
#获取字符集编码方式
import chardet
#正则表达式
import re
#Request 对象
req = urllib.request.Request("http://meng.uno/")
data = None
try:
#得到Response
response = urllib.request.urlopen(req,data)
#读出response == 请求文件的全部字符
html = response.read()
#获取这个response的编码方式
charset = chardet.detect(html)
print("编码方式:",charset)
#以这种编码方式解码打印
html = html.decode(charset.get("encoding"))
print(html)
urls = re.findall('href=\"https*://w*\.*meng\.uno/.*?\"', html,re.S)
uris = re.findall('href=\"/[^/].*?[^\.]\"',html, re.S)
for item in urls:
print(item[6:-1])
for item in uris:
if ".html" in item:
print("http://www.meng.uno"+item[6:-1])
elif '.' in item:
continue
else:
print("http://www.meng.uno"+item[6:-1])
except urllib.error.HTTPError as e:
if hasattr(e, 'code'):
print("HTTPError")
print(e.code)
elif hasattr(e, 'reason'):
print("URLError")
print(e.reason)

模拟真实环境的爬虫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import urllib
#访问网址
url = 'http://www.whatismyip.com.tw/'
#这是代理IP
proxy = {'https':'110.73.48.189:8123'}
#创建ProxyHandler
proxy_support = urllib.request.ProxyHandler(proxy)
#创建Opener
opener = urllib.request.build_opener(proxy_support)
#添加User Angent
opener.addheaders = [('User-Agent','Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36')]
#安装OPener
urllib.request.install_opener(opener)
#使用自己安装好的Opener
response = urllib.request.urlopen(url)
#读取相应信息并解码
html = response.read().decode("utf-8")
#打印信息
print(html)

通过队列获取网站所有URL的爬虫

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
47
48
49
50
#python系统关于队列的包
import queue
#获取URL的包
import urllib
#获取字符集编码方式
import chardet
#正则表达式
import re
initial_page = "http://www.meng.uno"

url_queue = queue.Queue()
seen = set()

seen.add(initial_page)
url_queue.put(initial_page)

def extract_urls(url):
req = urllib.request.Request(url)
#得到Response
response = urllib.request.urlopen(req)
#读出response == 请求文件的全部字符
html = response.read()
#获取这个response的编码方式
charset = chardet.detect(html)
#以这种编码方式解码打印
html = html.decode(charset.get("encoding"))
urls = re.findall('href=\"https*://w*\.*meng\.uno/.*?\"', html,re.S)
uris = re.findall('href=\"/[^/].*?[^\.]\"',html, re.S)
tempseen = set()
for item in urls:
tempseen.add(item[6:-1])
for item in uris:
if ".html" in item:
tempseen.add("http://www.meng.uno"+item[6:-1])
elif '.' in item:
continue
else:
tempseen.add("http://www.meng.uno"+item[6:-1])
return tempseen

while(True): #一直进行直到海枯石烂
if url_queue.qsize()>0:
current_url = url_queue.get() #拿出队例中第一个的url
print(current_url) #把这个url代表的网页存储好
for next_url in extract_urls(current_url): #提取把这个url里链向的url
if next_url not in seen:
seen.add(next_url)
url_queue.put(next_url)
else:
break

这里先简单解释,以后有实际项目会再补充!



本文链接: http://home.meng.uno/articles/51d32f19/ 欢迎转载!

© 2018.02.08 - 2020.10.14 Mengmeng Kuang  保留所有权利!

UV : | PV :

:D 获取中...

Creative Commons License