爬虫
网络爬虫,是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。
Python如何访问互联网
使用Urllib库
- URL
一般格式:
protocol://hostname[:port]/path/[;parameters][?query]#fragment
中括号中为可选项
url由三部分组成:
(1)协议:http,https,ftp,file,ed2k…
(2)存放资源的服务器的域名系统或IP地址(有时需要包含端口号,各传输协议都有默认的端口号,如http默认端口号80)
(3)资源的具体地址,如目录或文件名等 - urlopen函数
urllib.request 文档
urllib.request.urlopen(url, data=None,[timeout,]*, cafile=None, capath=None, cadeful=False)
>>> import urllib.request
>>> response = urllib.request.urlopen("https://www.csdn.net/")
>>> html = response.read() # 读取网页内容
>>> html = html.decode("utf-8") # 以“utf-8”格式编码显示
>>> print(html) # 打印内容
实战
- 下载一张猫图
import urllib.request
response = urllib.request.urlopen('http://placekitten.com/g/300/300')
cat_img = response.read()
with open('cat_300_300.jpg', 'wb') as f:
f.write(cat_img)
urlopen的参数可以是一个字符串地址或Request对象,字符串地址会默认自动转换成一个Request对象。
上面的代码还可修改为
import urllib.request
req = urllib.request.Request('http://placekitten.com/g/300/300')
response = urllib.request.urlopen(req)
# 实际上如果直接用urlopen的话,会默认执行以上两句
cat_img = response.read()
with open('cat_300_300.jpg', 'wb') as f:
f.write(cat_img)
response
- response.geturl()
得到访问的地址 - response.info()
返回一个HTTPMessage的对象
打印
包含了远程服务器返回的header信息 - response.getcode()
返回HTTP的状态,200即为可以正常响应
运用词典翻译文本
打开有道翻译
进入浏览器的开发者工具,点击Network
输入内容
可以看到开发者工具界面有很多浏览器和客户端的通信内容
method一栏可以看到有GET和POST,GET是指从服务器请求获得数据,而POST是向指定服务器提交被处理的数据。(如果没有Method一栏 可以右击Name处将Method勾选)
点击一个POST文件,点击Preview
发现其中有我们需要的翻译内容
先来分析以下其中的内容
-
Headers
Request URL:即是urlopen要打开的地址,urlopen要打开的不是浏览器地址栏的地址,在此处是实现翻译机制的地址
Request Method:请求的方法,在这里是POST形式
Status Code:状态码
Remote Address:IP地址加打开的端口号
Referrer Policy:Referrer策略,no-referrer-when-downgrade表示从https协议降为http协议时不发送referrer给跳转网站的服务器。
Request Headers 是客户端,此处为浏览器,被服务端用来判断是否非人类访问(防止使用代码大批量访问网站,造成服务器压力过大的问题)
Request Headers 中有一个 User-Agent 用来识别是浏览器访问还是代码访问,其中显示你访问所使用的的系统架构、浏览器实现的核心平台,使用的浏览器类型和版本号,所以当你使用 Python 来访问的时候就会被 User-Agent 识别并屏蔽,不过 User-Agent 是可以进行自定义的,所以这并不能阻拦我们用 Python 进行访问。
From Data: 表单数据,即POST提交的主要内容
可以看出 i 后即为我们输入的待翻译的内容 -
操作
利用Python提交POST表单
urllib.parse 模块
import urllib.request
import urllib.parse # 用于解析
url= 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
# url: 此处填写Headers里的Request URL的地址
# 本来是http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule,但是会出现errorCode:50的错误
# 所以去掉了_o
data = {}
data ['i'] = 'hello python'
data ['from'] = 'AUTO'
data ['to'] = 'AUTO'
data ['smartresult'] = 'dict'
data ['client'] = 'fanyideskweb'
data ['salt'] = '15795035731918'
data ['sign'] = 'f46e6f89190536f01015ca437b0d5157'
data ['ts'] = '1579503573191'
data ['bv'] = 'd7923968d7ea53caf8c2c98c014700ff'
# ts是时间戳
# salt是时间戳后面加上一位随机数,
# sign是由md5加密过的内容
# bv是浏览器头部信息MD5的值
data ['doctype'] = 'json'
data ['version'] = '2.1'
data ['keyfrom'] = 'fanyi.web'
data ['action'] = 'FY_BY_REALTlME'
#data [''] = ''
# data是一个字典: 此处填入Form Data的内容
data = urllib.parse.urlencode(data).encode('utf-8')
# 将编码后的值赋给data,这里encode就是将unicode格式转化为utf-8的编码形式
response = urllib.request.urlopen(url, data)
# 发出请求,将响应存入response
html = response.read().decode('utf-8')
# windows系统下不用decode也没问题,其他系统可能就需要加decode
# 由于read后得到的是utf-8编码形式的文件,decode就是把其他编码形式变为unicode形式,encode就是把unicode形式变为其他形式
# 这里的decode即为解码
print(html)
执行后输出了我们需要的内容
输出的是json结构的数据,json是一种轻量级的数据交换格式,即用字符串的形式把Python数据结构给封装起来,就得到了json。
这个字符串内部实际上是包含了一个字典,字典中键“translateResult”的值内部是一个列表中的列表中的字典。
变为用户输入数据,并简化输出
import urllib.request
import urllib.parse # 用于解析
import json
content = input("请输入需要翻译的内容:")
url= 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
# url: 此处填写Headers里的Request URL的地址
# 本来是http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule,但是会出现errorCode:50的错误
# 所以去掉了_o
data = {}
data ['i'] = content
data ['from'] = 'AUTO'
data ['to'] = 'AUTO'
data ['smartresult'] = 'dict'
data ['client'] = 'fanyideskweb'
data ['salt'] = '15795035731918'
data ['sign'] = 'f46e6f89190536f01015ca437b0d5157'
data ['ts'] = '1579503573191'
data ['bv'] = 'd7923968d7ea53caf8c2c98c014700ff'
# ts是时间戳
# salt是时间戳后面加上一位随机数,
# sign是由md5加密过的内容
# bv是浏览器头部信息MD5的值
data ['doctype'] = 'json'
data ['version'] = '2.1'
data ['keyfrom'] = 'fanyi.web'
data ['action'] = 'FY_BY_REALTlME'
#data [''] = ''
# data是一个字典: 此处填入Form Data的内容
data = urllib.parse.urlencode(data).encode('utf-8')
# 将编码后的值赋给data,这里encode就是将unicode格式转化为utf-8的编码形式
response = urllib.request.urlopen(url, data)
# 发出请求,将响应存入response
html = response.read().decode('utf-8')
# windows系统下不用decode也没问题,其他系统可能就需要加decode
# 由于read后得到的是utf-8编码形式的文件,decode就是把其他编码形式变为unicode形式,encode就是把unicode形式变为其他形式
# 这里的decode即为解码
target = json.loads(html)
print("翻译结果:%s" % (target['translateResult'][0][0]['tgt']))
得到结果
但是此代码还不能应用于现实生产环境中,因为容易被服务器发现是非人访问或ip访问过于频繁而被屏蔽。
隐藏
为了使我们的程序不会被服务器判定为非人类而被屏蔽,需要对代码进行隐藏。
服务器检查链接,通常情况下是检查链接的Headers里的User-Agent 来判断访问是来自于代码还是来自于浏览器,所以可以通过修改Headers来模拟正常的浏览器访问。
在Python中修改headers要用到类urllib.request.Request
Request
urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
- headers
headers 必须是一个字典,可以通过两种途径设置
(1)直接设置一个字典,作为参数传给request
# 方法一:直接将浏览器中的User-Agent的值传入新建的字典,并作为参数传给request
import urllib.request
import urllib.parse # 用于解析
import json
content = input("请输入需要翻译的内容:")
url= 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
head = {}
head['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
data = {}
data ['i'] = content
data ['from'] = 'AUTO'
data ['to'] = 'AUTO'
data ['smartresult'] = 'dict'
data ['client'] = 'fanyideskweb'
data ['salt'] = '15795035731918'
data ['sign'] = 'f46e6f89190536f01015ca437b0d5157'
data ['ts'] = '1579503573191'
data ['bv'] = 'd7923968d7ea53caf8c2c98c014700ff'
data ['doctype'] = 'json'
data ['version'] = '2.1'
data ['keyfrom'] = 'fanyi.web'
data ['action'] = 'FY_BY_REALTlME'
data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(url, data, head)
response = urllib.request.urlopen(req)
html = response.read().decode('utf-8')
target = json.loads(html)
target = target['translateResult'][0][0]['tgt']
print("翻译结果:", target)
(2)在request生成后,通过调用add_header()把它添加进去
Request.add_header(key, val)
# 方法二:
import urllib.request
import urllib.parse # 用于解析
import json
content = input("请输入需要翻译的内容:")
url= 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
data = {}
data ['i'] = content
data ['from'] = 'AUTO'
data ['to'] = 'AUTO'
data ['smartresult'] = 'dict'
data ['client'] = 'fanyideskweb'
data ['salt'] = '15795035731918'
data ['sign'] = 'f46e6f89190536f01015ca437b0d5157'
data ['ts'] = '1579503573191'
data ['bv'] = 'd7923968d7ea53caf8c2c98c014700ff'
data ['doctype'] = 'json'
data ['version'] = '2.1'
data ['keyfrom'] = 'fanyi.web'
data ['action'] = 'FY_BY_REALTlME'
data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(url, data)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36')
response = urllib.request.urlopen(req)
html = response.read().decode('utf-8')
target = json.loads(html)
target = target['translateResult'][0][0]['tgt']
print("翻译结果:", target)
至此完成了服务器对访问对象的检测问题的处理,然而在实际情况中,代码访问服务器进行爬虫,通常情况下的持续访问的速度很快,导致服务器负载,因此服务器会将此ip进行屏蔽,或者返回一个验证码页面来进行非人类行为判断。
针对以上问题有两种解决方法:
(1)延迟提交时间
(2)使用代理
延迟提交时间
# 每次翻译后等5秒再次输入
import urllib.request
import urllib.parse # 用于解析
import json
import time
while True:
content = input("请输入需要翻译的内容(输入“q!”退出程序):")
if content == 'q!':
break
url= 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
data = {}
data ['i'] = content
data ['from'] = 'AUTO'
data ['to'] = 'AUTO'
data ['smartresult'] = 'dict'
data ['client'] = 'fanyideskweb'
data ['salt'] = '15795035731918'
data ['sign'] = 'f46e6f89190536f01015ca437b0d5157'
data ['ts'] = '1579503573191'
data ['bv'] = 'd7923968d7ea53caf8c2c98c014700ff'
data ['doctype'] = 'json'
data ['version'] = '2.1'
data ['keyfrom'] = 'fanyi.web'
data ['action'] = 'FY_BY_REALTlME'
data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(url, data)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36')
response = urllib.request.urlopen(req)
html = response.read().decode('utf-8')
target = json.loads(html)
target = target['translateResult'][0][0]['tgt']
print("翻译结果:", target)
time.sleep(5)
代理
代理服务器是介于浏览器和Web服务器之间的一台服务器,浏览器向代理服务器发出请求,Request信号会先送到代理服务器,由代理服务器来取回浏览器所需要的信息并传送给浏览器。
- 步骤
(1)参数是一个字典{ ‘类型’ : ‘代理ip:端口号’ }
proxy_support = urllib.request.ProxyHandler({})
(2)定制、创建一个 opener
opener = urllib.request.build_opener(proxy_support)
可以定制opener来用代理ip进行访问
(3)
a.安装 opener【本方法用于替换掉默认的opener】
urllib.request.install_opener(opener)
b. 调用 opener【本方法用于临时或特殊情况下使用,不修改默认opener】
opener.open(url)
import urllib.request
url = 'https://www.ipip.net/ip.html'
# 用于查询当前自己使用的ip
proxy_support = urllib.request.ProxyHandler({'https':'222.95.144.130:3000'})
# 从网络上查询到的免费代理ip和端口号
opener = urllib.request.build_opener(proxy_support)
opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36')]
urllib.request.install_opener(opener)
response = urllib.request.urlopen(url)
html = response.read().decode('utf-8')
print(html)
还可以建立一个ip列表,把采集到的ip地址加进去,以供使用
import urllib.request
import random
url = 'https://www.ipip.net/ip.html'
# 用于查询当前自己使用的ip
iplist = ['58.212.40.126:9999', '113.128.9.171:9999', '61.145.48.94:9999']
proxy_support = urllib.request.ProxyHandler({'https':random.choice(iplist)})
# 从网络上查询到的免费代理ip和端口号
opener = urllib.request.build_opener(proxy_support)
opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36')]
urllib.request.install_opener(opener)
response = urllib.request.urlopen(url)
html = response.read().decode('utf-8')
print(html)
爬取网页上的图片
- 要求:爬取 http://jandan.net/zoo/ 上的10页图片
- 操作:打开网页后,进入开发者工具
检查页码处代码
查看下一页的网页地址格式
检查图片处代码,查看图片处代码格式
由此写爬取代码
# =============================================================================
# 爬取网站上的图片
# =============================================================================
import urllib.request
import os
def url_open(url):
# =============================================================================
# 打开网址,爬取内容
# =============================================================================
req = urllib.request.Request(url)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36')
response = urllib.request.urlopen(url)
html = response.read() # 这里不解码是因为,如果解码了那么无法保存图片
return html
def get_url(url, x):
# =============================================================================
# 查找下一页,这里的查找方式有局限性,只是单纯针对jandan网的页面设计
# 不具有可移植性,不同网站不同查找方式
# =============================================================================
html = url_open(url).decode('utf-8')
a = html.find(x) + 24
b = html.find('#', a)
return html[a:b]
def find_imgs(url):
# =============================================================================
# 取当前页面的所有图片的链接,并保存到列表中
# =============================================================================
html = url_open(url).decode('utf-8')
img_addrs = [] # 存放图片地址的列表
a = html.find('img src=') # 找到图片的地址
while a != -1: # 循环找图
b = html.find('.jpg', a, a+255) # 找到.jpg结尾且不超过255个字符的(即不是jpg格式的图片跳过且返回-1)
if b != -1:
img_addrs.append('http:'+ html[a+9:b+4]) # 偏移即a的加(len('img src=')+1),b的加(len('.jpg'))
else:
b = a + 9 # 防止一直在一个地方循环
a = html.find('img src=', b) # 从b开始继续找
# =============================================================================
# 测试打印列表
# for each in img_addrs:
# print(each)
# =============================================================================
return img_addrs
def save_imgs(folder, img_addrs):
# =============================================================================
# 保存图片到指定目录
# =============================================================================
for each in img_addrs:
filename = each.split('/')[-1] # 切割出最后一个反斜杠后的字符作为名字
with open(filename, 'wb') as f:
img = url_open(each)
f.write(img)
def download_pic(folder='img', page=10):
os.mkdir(folder) # 建立目录
os.chdir(folder) # 改变当前的工作目录到folder='img'
url = "http://jandan.net/zoo/" # 目标网站
for i in range(10): # 爬取10页内容
if i == 10 :
break
page_url = url # 当前循环爬取的页面
img_addrs = find_imgs(page_url)
save_imgs(folder, img_addrs)
page_str_ed = get_url(url, 'Older Comments') # 取下一页
url = 'http://' + page_str_ed + '#comments' # 保存下次循环地址
if __name__ == '__main__':
download_pic()
运行结果
来源:CSDN
作者:雨山林稀
链接:https://blog.csdn.net/qq_36756476/article/details/104031832