文章目录
- python爬虫-----网络数据采集
- 一、爬虫基础案例:图片下载器
- 遇到的问题:
- 二、网络数据采集
- 2.网络数据采集之requests库(常用)
- requests方法
- response对象
- 高级应用一: 添加 headers
- 2.fake_useragent:
- 高级应用二: 代理设置
- 报错:
- 项目案例一: 京东商品的爬取
- 项目案例二: 百度/360搜索关键词提交
- 常见问题:
- 小练习
python爬虫-----网络数据采集
一、爬虫基础案例:图片下载器
网络爬虫抓取过程可以理解为模拟浏览器操作的过程,所以深入理解 HTTP 协议更有利于爬虫的学习,并且该部分是面试官非常喜欢问的部分,很重要,详情请见:
import os
import re
import requests
from colorama import Fore
def download_image(url, keyword):
"""
下载图片
:param url: 百度图片的网址
:return: Bool
"""
# 1. 向服务器发起HTTP请求
response = requests.get(url)
# 2. 获取服务器端的响应信息
# 响应信息: status_code, text, url
data = response.text
# 3. 编写正则表达式,获取图片的网址
# "ObjURL":"http:\/\/img2.imgtn.bdimg.com\/it\/u=3459137507,1368309920&fm=214&gp=0.jpg"
# 获取到的: http:\/\/img2.imgtn.bdimg.com\/it\/u=3459137507,1368309920&fm=214&gp=0.jpg
# 正则的语法: .代表除了\n之外的任意字符, *代表前一个字符出现0次或者无数次. ?非贪婪模式
pattern = r'"ObjURL":"(.*?)"'
# 4. 根据正则表达式寻找符合条件的图片网址.
image_urls = re.findall(pattern, data)
# 5. 下载猫的图片到本地
index = 1
for image_url in image_urls:
# 转义字符: \n, \t, \ , \\,
image_url = image_url.replace('\\', '')
print(image_url) # 'xxxx.jpg xxxx.png'
# response.text返回unicode的文本信息, response.text返回bytes类型的信息
try:
response = requests.get(image_url)
except Exception as e:
print(Fore.RED + "[-]下载失败: %s" % (image_url))
else:
old_image_filename = image_url.split('/')[-1]
if old_image_filename:
image_format = old_image_filename.split('.')[-1]
# jpeg?imageview&thumbnail=550x0
if '?' in image_format:
image_format = image_format.split('?')[0]
else:
image_format = 'jpg'
# 生成图片的存储目录, keyword='cat', 'dog', 'python django'
keyword = keyword.replace(' ', '_')
print(keyword)
if not os.path.exists(keyword):
os.mkdir(keyword)
image_filename = os.path.join(keyword, str(index) + '.' + image_format)
with open(image_filename, 'wb') as f:
f.write(response.content)
print(Fore.BLUE + "[+] 保存图片%s.jpg成功" % (index))
index += 1
if __name__ == '__main__':
keyword = input("请输入批量下载图片的关键字: ")
# url地址里面参数信息可以长可以短, 有的参数可以省略的。
url = 'http://image.baidu.com/search/index?tn=baiduimage&word=' + keyword
print(Fore.BLUE + '[+] 正在请求网址: %s' % (url))
download_image(url, keyword)
遇到的问题:
1.问题:[-]下载失败: http://img1.imgtn.bdimg.com/it/u=2384096151,2805439088&fm=214&gp=0.jpg
原因:反斜杠不识别
解决:遍历image_urls时将这里的反斜杠替换成空即可 image_url = image_url.replace("\\", "")
理解:要把反斜杠替换为
二、网络数据采集
1.网络数据采集之urllib库
2.网络数据采集之requests库(常用)
requests官方网址: https://requests.readthedocs.io/en/master/
requests方法
我们自己写一个服务器来测试,就不会有反爬
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
# 获取用户GET提交的数据信息
print(request.args)
return 'index: %s' % (request.args)
@app.route('/post/', methods=['POST'])
def post():
# 获取用户POST提交的数据信息
print(request.form)
# return 'post info: %s' % (request.form)
username = request.form.get('username')
password = request.form.get('password')
if username == 'admin' and password == 'westos123':
return 'login success'
else:
return 'login failed'
if __name__ == '__main__':
app.run()
request中的 get和post
from urllib.error import HTTPError
import requests
def get():
# get方法可以获取页面数据,也可以提交非敏感数据
# url = 'http://127.0.0.1:5000/'
# url = 'http://127.0.0.1:5000/?username=fentiao&page=1&per_page=5'
url = 'http://127.0.0.1:5000/'
try:
params = {
'username': 'fentiao',
'page': 1,
'per_page': 5
}
response = requests.get(url, params=params)
print(response.text, response.url)
# print(response)
# print(response.status_code)
# print(response.text)
# print(response.content)
# print(response.encoding)
except HTTPError as e:
print("爬虫爬取%s失败: %s" % (url, e.reason))
def post():
url = 'http://127.0.0.1:5000/post'
try:
data = {
'username': 'admin',
'password': 'westos12'
}
response = requests.post(url, data=data)
print(response.text)
except HTTPError as e:
print("爬虫爬取%s失败: %s" % (url, e.reason))
if __name__ == '__main__':
get()
# post()
#get方法结果
index: ImmutableMultiDict([('username', 'fentiao'), ('page', '1'), ('per_page', '5')]) http://127.0.0.1:5000/?username=fentiao&page=1&per_page=5
#post方法结果
login failed
运行我们的服务器,在运行request代码
response对象
Response对象包含服务器返回的所有信息,也包含请求的Request信息。
高级应用一: 添加 headers
1.有些网站访问时必须带有浏览器等信息,如果不传入headers就会报错。
headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0'}
response = requests.get(url, headers=headers)
2.fake_useragent:
UserAgent是识别浏览器的一串字符串,相当于浏览器的身份证,在利用爬虫爬取网站数据时,频繁更换UserAgent可以避免触发相应的反爬机制。fake-useragent
对频繁更换UserAgent提供了很好的支持,可谓防反扒利器。
UserAgent实质上是从网络获取所有的用户代理, 再通过random随机选取一个用户代理。
user_agent = UserAgent().random
安装:
pip install fake_useragent
3.测试
自己写的服务器
启动服务器,运行代码模拟浏览器访问服务器
"""
FileName: FlaskApp
Date: 09 18
Author: lvah
Connect: 976131979@qq.com
Description:
"""
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
# 获取用户GET提交的数据信息
print(request.args)
print("客户端请求的user_agent: ", request.user_agent)
return 'index: %s' % (request.args)
@app.route('/post/', methods=['POST'])
def post():
# 获取用户POST提交的数据信息
print(request.form)
# return 'post info: %s' % (request.form)
username = request.form.get('username')
password = request.form.get('password')
if username == 'admin' and password == 'westos123':
return 'login success'
else:
return 'login failed'
if __name__ == '__main__':
app.run()
启动服务器,运行代码模拟浏览器访问服务器
import requests
from fake_useragent import UserAgent
def add_headers():
# headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0'}
# UserAgent实质上是从网络获取所有的用户代理, 再通过random随机选取一个用户代理。
# https://fake-useragent.herokuapp.com/browsers/0.1.11
ua = UserAgent()
# 默认情况下, python爬虫的用户代理是python-requests/2.22.0。
response = requests.get('http://127.0.0.1:5000', headers={'User-Agent': ua.random})
print(response)
if __name__ == '__main__':
add_headers()
#运行结果
<Response [200]>
#去服务器端查看结果
ImmutableMultiDict([])
客户端请求的user_agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36
高级应用二: 代理设置
在进行爬虫爬取时,有时候爬虫会被服务器给屏蔽掉,这时采用的方法主要有降低访问时间,通过代理IP访问。ip可以从网上抓取,或者某宝购买。
比较有名的一个免费的IP代理是:西刺IP代理 https://www.xicidaili.com/nn/
proxies = { "http": "http://127.0.0.1:9743", "https": "https://127.0.0.1:9743",}
response = requests.get(url, proxies=proxies)
测试:
服务器
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
# 获取用户GET提交的数据信息
print(request.args)
print("客户端请求的user_agent: ", request.user_agent)
# print("客户端请求的IP:", request.remote_addr)
return 'index: %s' % (request.args, request.remote_addr)
@app.route('/post/', methods=['POST'])
def post():
# 获取用户POST提交的数据信息
print(request.form)
# return 'post info: %s' % (request.form)
username = request.form.get('username')
password = request.form.get('password')
if username == 'admin' and password == 'westos123':
return 'login success'
else:
return 'login failed'
if __name__ == '__main__':
app.run(debug=True)
import requests
from fake_useragent import UserAgent
ua = UserAgent()
proxies = {
'http': 'http://222.95.144.65:3000',
'https': 'https://182.92.220.212:8080'
}
response = requests.get('http://47.92.255.98:8000',
headers={'User-Agent': ua.random},
proxies=proxies
)
print(response)
# 这是因为服务器端会返回数据: get提交的数据和请求的客户端IP
# 如何判断是否成功? 返回的客户端IP刚好是代理IP, 代表成功。
print(response.text)
报错:
1.报错:requests.exceptions.ProxyError: HTTPConnectionPool(host=‘222.95.144.65’, port=3000): Max retries exceeded with url: http://47.92.255.98:8000/ (Caused by ProxyError(‘Cannot connect to proxy.’, NewConnectionError(’<urllib3.connection.HTTPConnection object at 0x000001F379E31B70>: Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接。’)))
原因:此IP访问次数超过限制
解决:去西刺IP重新复制新的ip和端口在测试
项目案例一: 京东商品的爬取
from urllib.error import HTTPError
import requests
from colorama import Fore
from fake_useragent import UserAgent
def download_page(url, parmas=None):
try:
ua = UserAgent()
headers = {'User-Agent': ua.random}
response = requests.get(url, params=parmas, headers=headers)
except HTTPError as e:
print(Fore.RED + '[-] 爬取网站%s失败: %s' % (url, e.reason))
return None
else:
# content返回的是bytes类型
return response.content
def download_file(content=b'', filename='jd.html'):
"""
:param content: 要写入本地的html字符bytes类型
:param filename: 写入本地的文件名
:return:
"""
with open(filename, 'wb') as f:
f.write(content)
print(Fore.GREEN + '[+]写入文件%s成功' % (filename))
if __name__ == '__main__':
url = 'https://item.jd.com/100012015170.html'
html = download_page(url)
download_file(content=html)
#项目案例二: 百度/360搜索关键词提交
# url = 'https://www.so.com/s'
# params = {
# 'q': 'python'
# }
# content = download_page(url, params)
# download_file(content)
运行代码
目录生成一个jd.html文件
用浏览器打开
可以看到是本地的地址:
项目案例二: 百度/360搜索关键词提交
百度的关键词接口:https://www.baidu.com/baidu?wd=xxx&tn=monline_4_dg
360的关键词接口:http://www.so.com/s?q=keyword
1.需求分析,至少要实现两个功能:一是搜索图片,二是自动下载
2.分析网页 http://image.baidu.com/search/index?tn=baiduimage&word=cat (注意:其中可以删除不用的参数,不影响页面的加载) 源代码,配合F12
3.编写正则表达式或者其他解析器代码
4.存储数据到本地
5.正式编写python爬虫代码
页面的分析是十分重要的,不同需求对应不同的URL,不同URL的源码显然是不一样的,所以掌握如何分析页面是成功爬虫的第一步。该页面的源码分析如下图所示:
import os
import re
import requests
from colorama import Fore
def download_image(url, keyword):
"""
下载图片
:param url: 百度图片的网址
:return: Bool
"""
# 1. 向服务器发起HTTP请求
response = requests.get(url)
# 2. 获取服务器端的响应信息
# 响应信息: status_code, text, url
data = response.text # 获取页面源码
# 3. 编写正则表达式,获取图片的网址
# data = ...[{"ObjURL":"http:\/\/images.freeimages.com\/images\/large-previews\/3bc\/calico-cat-outside-1561133.jpg",....}]...
# 需要获取到的是: http:\/\/images.freeimages.com\/images\/large-previews\/3bc\/calico-cat-outside-1561133.jpg
# 正则的语法: .代表除了\n之外的任意字符, *代表前一个字符出现0次或者无数次. ?代表非贪婪模式
pattern = r'"objURL":"(.*?)"'
# 4. 根据正则表达式寻找符合条件的图片网址.
image_urls = re.findall(pattern, data)
# 5. 根据图片网址下载猫的图片到本地
index = 1
for image_url in image_urls:
print(image_url) # 'xxxx.jpg xxxx.png'
# response.text 返回 unicode 的文本信息, response.text 返回 bytes 类型的信息
try:
response = requests.get(image_url) # 向每一个图片的url发起HTTP请求
except Exception as e:
print(Fore.RED + "[-] 下载失败: %s" % (image_url))
else:
old_image_filename = image_url.split('/')[-1]
if old_image_filename:
# 获取图片的后缀
image_format = old_image_filename.split('.')[-1]
# 处理 url 为...jpeg?imageview&thumbnail=550x0 结尾(传参)的情况
if '?' in image_format:
image_format = image_format.split('?')[0]
else:
image_format = 'jpg'
# 生成图片的存储目录
keyword = keyword.split(' ', '-')
if not os.path.exists(keyword):
os.mkdir(keyword)
image_filename = os.path.join(keyword, str(index) + '.' + image_format)
# 保存图片
with open(image_filename, 'wb') as f:
f.write(response.content)
print(Fore.BLUE + "[+] 保存图片%s.jpg成功" % (index))
index += 1
if __name__ == '__main__':
keyword = input("请输入批量下载图片的关键字: ")
url = 'http://image.baidu.com/search/index?tn=baiduimage&word=' + keyword
print(Fore.BLUE + '[+] 正在请求网址: %s' % (url))
download_image(url, keyword)
运行上面代码结果:
常见问题:
(1)为什么只有30张图片,百度出来的不止30张
百度图片是响应式的,不断下拉会不断加载新的图片,也就是说浏览器中的页面是经过JS处理数据后生成的结果,涉及Ajax爬虫内容在此不做详细说明。
(2)在搜索页面下点开的单个图片的 url 与程序中获取的 ObjURL 是不一致的
这可能是百度经过缓存处理的结果,每个图片本质上是“外网的”,非百度的,所以在程序中选择向真正存储图片的 url 发起 HTTP 请求。
小练习
需求分析:通过输入要搜索的关键字,获取页面并将页面保存到本地。
结果展示:输入apologize 后,将获取到的页面存入本地,打开本地的html
代码:
from urllib.error import HTTPError
import requests
from fake_useragent import UserAgent
from colorama import Fore
def down_loadPage(url,params):
try:
ua=UserAgent()
headers={'User-Agent':ua.random}
response=requests.get(url,headers=headers,params=params)
except HTTPError as e:
print(Fore.RED + '[-] 爬取网站%s失败: %s' % (url, e.reason))
return None
else:
return response.content
def down_filename(content=b'',filename='hello.html'):# response.content是bytes类型
with open(filename,'wb') as f :
f.write(content)
print(Fore.GREEN + '[+]写入文件%s成功' % (filename))
if __name__ == '__main__':
params = input('请输入要查询的字符:')
url='http://www.youdao.com/w/eng/' +params+ '/#keyfrom=dict2.index'
content=down_loadPage(url,params) #获取网页内容
down_filename(content) #将内容下载到本地