Python 爬虫实战与技巧分享–urllib
在当今信息时代,数据的价值日益凸显。Python 爬虫作为一种强大的数据获取工具,能够帮助我们从互联网上抓取各种有价值的信息。本文将结合具体代码示例,深入探讨 Python 爬虫的相关知识和关键要点。
一、Python 爬虫基础
Python 爬虫是一种使用 Python 编程语言编写的程序,它能够自动地在互联网上浏览网页,并提取出有价值的信息。通常,Python 爬虫的工作流程包括以下几个步骤:
- 定义目标 URL:确定要爬取的网页地址。
- 发送请求:模拟浏览器向服务器发送请求,获取网页内容。
- 解析内容:从响应中提取出有价值的信息,可以使用正则表达式、XPath、BeautifulSoup 等库来实现。
- 存储数据:将提取出的信息存储到本地文件、数据库或其他存储介质中。
二、Python 爬虫的工作原理
Python 爬虫的工作原理主要包括以下几个步骤:
- 定义目标 URL:首先,我们需要确定要爬取的网页地址。这个地址就是我们的目标访问地址,就像我们在浏览器中输入的网址一样。
- 发送请求:接下来,我们需要模拟浏览器向服务器发送请求。这个请求可以是 GET 请求(获取网页内容)或 POST 请求(提交数据)等。
- 获取响应:服务器接收到请求后,会返回一个响应。这个响应包含了网页的内容、状态码等信息。
- 解析内容:我们需要从响应中提取出有价值的信息。这可以通过解析 HTML 页面、使用正则表达式、使用 XPath 或 BeautifulSoup 等库来实现。
- 存储数据:最后,我们可以将提取出的信息存储到本地文件、数据库或其他存储介质中,以便后续分析和使用。
三、代码示例详解
1、使用 urllib
获取百度首页源码
以下是使用 urllib
库获取百度首页源码的代码:
import urllib.request# (1)定义一个 url->就是我们的目标访问地址
url = 'http://www.baidu.com'# (2)模拟浏览器向服务器发送请求
response = urllib.request.urlopen(url)# (3)获取响应页面中的源码
# read 方法返回的是字节形式的二进制数据
# 需要解码:将二进制数据转化成字符串--》二进制-->字符串 decode('编码的格式‘)
response = response.read().decode('utf-8')# (4)打印
print(response)
在这个例子中,我们首先导入了 urllib.request
模块,然后定义了百度首页的 URL。接着,使用 urlopen
方法发送请求并获取响应对象。最后,通过 read
方法读取响应内容,并使用 decode
方法将二进制数据解码成字符串后打印输出。
2、urllib
的一个类型和六个方法
以下代码展示了 urllib
的一个类型 HTTPResponse
和六个方法:
import urllib.requesturl = 'http://www.baidu.com'response = urllib.request.urlopen(url)# 一个类型 HTTPResponse
print(type(response)) # http.client.HTTPResponse 的类型# 六个方法
# 1.read(字节) 按照一个个字节读取(read 里面的数据表示返回多少个字节)
print(response.read())# 2.readline() 读取一行
print(response.readline())# 3.readlines() 一行一行读取直至读取完毕
print(response.readlines())# 4.response.getcode() 返回状态码
print(response.getcode()) # 200/404/500# 5.response.geturl()
print(response.geturl())# 6.获取一个状态信息的响应头
print(response.getheaders())
通过这个例子,我们可以了解到 urllib
响应对象的类型和各种方法的用途,如读取内容、获取状态码、URL 和响应头等。
3、使用 urlretrieve
下载文件
urllib.request.urlretrieve
方法可以用来下载网页、图片和视频等文件:
import urllib.request# 下载网页
url_page = 'http://www.baidu.com'
urllib.request.urlretrieve(url_page, 'baidu.html')# 下载图片
url_img = "https://www.bing.com/images/search?view=detailV2&itb=0"
urllib.request.urlretrieve(url=url_img, filename='baidu.jpg')# 下载视频
url_video = "https://tv.sohu.com/v/dXMvMzM1OTQxNzk3LzM5OTA4NDE3Ni5zaHRtbA==.html"
urllib.request.urlretrieve(url=url_video, filename='baidu.mp4')
这个方法非常方便,可以直接将指定 URL 的内容保存到本地文件中。
4、设置请求头
UA 介绍:UserAgent 中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的服务器能的操作系统及版本、CPU 类型、浏览器及版本、浏览器内核、浏览器渲染引擎、浏览器语言、浏览器插件等。
为了避免被网站识别为爬虫而被封禁,我们可以设置请求头来模拟浏览器行为。以下是设置请求头获取百度首页源码的代码:
import urllib.requesturl = 'https://www.baidu.com'headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
}# 因为 urlopen 方法中不能存储字典,所以 headers 不能传递出去,所以要定制请求对象request = urllib.request.Request(url, headers=headers) # 注意顺序和关键字顺序
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))
通过设置请求头中的 User-Agent
,我们可以伪装成浏览器,提高爬虫的稳定性。
5、URL 编码与解码
在处理中文参数时,需要进行 URL 编码和解码。以下是获取包含中文参数的网页源码的代码:
解释import urllib.request
import urllib.parse# 需求 获取 https://www.baidu.com/s?wd=周杰伦 的网页源码
url = 'https://www.google.com.hk/search?q='headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
}# 将周杰伦三个字替换成 Unicode 编码的形式 依赖于 urllib.parse
name = urllib.parse.quote('周杰伦')
url = url + name# 模拟向浏览器发送请求
request = urllib.request.Request(url, headers=headers)
req = urllib.request.urlopen(request)
print(req.read().decode('utf-8'))
这里使用 urllib.parse.quote
方法对中文进行编码,确保 URL 的正确性。
6、urlencode
应用场景
urllib.parse.urlencode
方法在处理多个参数时非常有用:
import urllib.request
import urllib.parse# urlencode 应用场景:多个参数的时候
base_url = 'https://www.google.com.hk/search?'
data = {'q': '周杰伦','sex': '男',
}url = base_url + urllib.parse.urlencode(data)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
}# 请求对象的定制
request = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(request)
html = response.read().decode('utf-8')
print(html)
这个方法可以将字典形式的参数编码成 URL 格式。
7、POST 请求与 JSON 数据处理
以下是使用 POST 请求并处理 JSON 数据的代码:
import urllib.request
import urllib.parse
import jsonurl = 'https://fanyi.baidu.com/sug'
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
}data = {'kw': 'spider'
}# post 的参数必须要进行编码 并且必须是字节型的数据 使用.encode('utf-8')
data = urllib.parse.urlencode(data).encode('utf-8')request = urllib.request.Request(url=url, data=data, headers=headers)
response = urllib.request.urlopen(request)
html = response.read().decode('utf-8')
print(html)print(type(html)) # str
obj = json.loads(html) # 将 str 转换成 json 对象
print(obj)
在这个例子中,我们对 POST 请求的参数进行编码,并将响应的 JSON 数据转换为 Python 对象进行处理。
8、复杂 POST 请求与 JSON 数据处理
以下是一个更复杂的 POST 请求并处理 JSON 数据的示例:
import urllib.request
import urllib.parse
import jsonurl = 'https://fanyi.baidu.com/v2transapi?from=en&to=zh'
data = {'from': 'en','to': 'zh','query': '1ovetrans','type': 'realtime','simple_means_flag': '3','sign': '198772.518981','token': '5483bfa652979b41f9c90d91f3de875d','domain': 'common'
}headers = {# 各种请求头信息
}# post 的参数必须要进行编码 并且必须是字节型的数据 使用.encode('utf-8')
data = urllib.parse.urlencode(data).encode('utf-8')request = urllib.request.Request(url=url, data=data, headers=headers)
response = urllib.request.urlopen(request)
html = response.read().decode('utf-8')
print(json.loads(html))
这个例子展示了在处理复杂 POST 请求时,如何设置多个参数和请求头,并解析响应的 JSON 数据。
9、基于 Ajax 的 GET 请求与数据保存
以下是基于 Ajax 的 GET 请求并将数据保存到本地的代码:
import urllib.request
import urllib.parse
import json# 基于 Ajax 的 get 请求url = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=0&limit=20'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
}request = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(request)
html = response.read().decode('utf-8')
data = json.loads(html)
# print(data)# 下载数据到本地
with open('douban.json', 'w', encoding='utf-8') as fp:# 使用 json.dumps 将列表转换为字符串json_str = json.dumps(data, ensure_ascii=False, indent=4)fp.write(json_str)print("数据已成功写入到 douban.json 文件中")
这个例子演示了如何处理基于 Ajax 的 GET 请求,并将获取的数据保存为 JSON 文件。
10、分页爬取数据
以下是实现分页爬取数据的代码:
import urllib.request
import urllib.parse
import jsondef create_request(page):base_url = ('https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&')data = {'start': (page - 1) * 20,'limit': 20,}data = urllib.parse.urlencode(data)url = base_url + data# print(url)headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'}request = urllib.request.Request(url, headers=headers)return requestdef fetch_page(requests):response = urllib.request.urlopen(requests)data = json.loads(response.read().decode('utf-8'))return datadef download_page(page, data):with open('douban' + str(page) + '.json', 'w', encoding='utf-8') as fp:json_str = json.dumps(data, ensure_ascii=False, indent=4)fp.write(json_str)print("目标:" + str(page) + ',已经下载完毕!')# 程序入口
if __name__ == '__main__':start_page = int(input("请输入起始的页码:"))end_page = int(input("请输入结束的页码:"))for page in range(start_page, end_page + 1):# 定制每一页的请求对象(url不同)requests = create_request(page)# 获取数据data = fetch_page(requests)# 保存数据download_page(page, data)
11、基于 Ajax 的 POST请求与数据保存
import urllib.request
import urllib.parse
import jsondef create_request(pageIndex):base_url = ('https://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname')data = {"cname": "成都","pid": "","pageIndex": pageIndex,"pageSize": "10"}data = urllib.parse.urlencode(data).encode("utf-8")headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'}request = urllib.request.Request(url=base_url, data=data, headers=headers)return requestdef fetch_page(requests):response = urllib.request.urlopen(requests)data = json.loads(response.read().decode('utf-8'))return datadef download_page(page, data):with open('kfc_page' + str(page) + '.json', 'w', encoding='utf-8') as fp:json_str = json.dumps(data, ensure_ascii=False, indent=4)fp.write(json_str)print("kfc目标:" + str(page) + ',已经下载完毕!')# 程序入口
if __name__ == '__main__':start_page = int(input("请输入起始的页码:"))end_page = int(input("请输入结束的页码:"))for pageIndex in range(start_page, end_page + 1):# 定制每一页的请求对象(url不同)requests = create_request(pageIndex)# 获取数据data = fetch_page(requests)# 保存数据download_page(pageIndex, data)
这段代码通过发送带有特定参数的请求,获取 KFC 在成都的门店信息,并将数据保存为 JSON 文件。关键要点在于构造合适的请求参数和处理 JSON 格式的响应数据。
12、异常处理–HTTPError
import urllib.request
import urllib.parse
import urllib.error
import jsonurl = ('https://blog.csdn.net/2202_76097976/article/details/142544121')
# 当主机地址和参数出现问题的时候,就会报出URLError
# url = ('https://blog1.csdn.net/2202_76097976/article/details/142544121')headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
}try:request = urllib.request.Request(url, headers=headers)response = urllib.request.urlopen(request)html = response.read().decode('utf-8')print(html)
except urllib.error.HTTPError as e:print("请稍后,系统正在升级。。。")
except urllib.error.URLError as e:print("主机或参数出现问题,请检查参数。。。")
这里展示了如何处理在爬虫过程中可能出现的错误,包括 HTTP 错误和 URL 错误。通过使用try...except
语句,可以增强程序的稳定性和可靠性。
13、绕过登录获取页面
适用的场景:
-
数据采集的时候需要绕过登陆然后进入到某个页面
-
个人信息页面是utf-8 但是还报错了编码错误
-
因为并没有进入到个人信息页面 而是跳转到了登陆页面 那么登陆页面不是utf-8所以报错
解释import urllib.request
import urllib.parse
import urllib.error
import jsonurl = 'https://cart.taobao.com/cart.htm' # 淘宝购物车(原定测试的是微博页面)headers = {# ':authority': 'h5api.m.taobao.com',# ':method': 'POST',# ':path': '/h5/mtop.trade.query.bag/5.0/?jsv=2.7.2&appKey=12574478&t=1728546606601&preventFallback=true',# ':scheme': 'https','accept': 'application/json',# 'accept-encoding': 'gzip, deflate, br, zstd','accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7','content-length': '347','content-type': 'application/x-www-form-urlencoded',# cookie中携带着你的登陆信息如果有登陆之后的cookie 那么我们就可以携带着cookie进入到任何页面'cookie': 'thw=cn; t=23c0c50b61858303b509f3ae5834bbef; hng=CN%7Czh-CN%7CCNY%7C156; _uetvid=89fd15e0500b11ef8886579c5bff040a','origin': 'https://cart.taobao.com','priority': 'u=1, i',# referer可以拿来防盗链(判断当前路劲是不是上一个链接进来的)(图片防盗链)'referer': 'https//cart.taobao.com/','sec-ch-ua': '"Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"','sec-fetch-dest': 'empty','sec-fetch-mode': 'cors','sec-fetch-site': 'same-site','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
}request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
data = response.read().decode('utf-8')
with open("cookie.html", "w", encoding="utf-8") as f:f.write(data)
这段代码尝试绕过登录进入淘宝购物车页面,通过设置特定的请求头,尤其是携带cookie
信息来实现。这展示了在某些情况下,为了获取特定页面的数据,需要模拟登录状态。
14、定制请求头
在爬取网页时,有时需要定制更高级的请求头来模拟真实的浏览器行为,避免被网站识别为爬虫而被封禁。例如,以下代码展示了如何使用Handler
来定制请求头访问百度并获取网页源码:
import urllib.request
import urllib.parse
import urllib.error
import jsonurl = 'http://www.baidu.com'headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
}
request = urllib.request.Request(url, headers=headers)# 获取 handler 对象
handler = urllib.request.HTTPHandler()# 获取 opener 对象
opener = urllib.request.build_opener(handler)response = opener.open(request)
data = response.read().decode('utf-8')
print(data)
在上述代码中,我们首先定义了一个包含特定user-agent
的请求头,然后创建了一个Request
对象并传入请求头。接着,通过HTTPHandler
和build_opener
创建了一个opener
对象,最后使用opener.open
方法发送请求并获取响应数据。
15、使用代理服务器
- 代理的常用功能:
- 突破自身 IP 访问限制,访问国外站点:对于一些因地域限制无法直接访问的国外网站,可以通过代理服务器来实现访问。
- 访问单位或团体内部资源:例如某大学 FTP,使用教育网内地址段免费代理服务器,可以用于对教育网开放的各类 FTP 下载上传以及各类资料查询共享等服务。
- 提高访问速度:代理服务器通常设置一个较大的硬盘缓冲区,当有外界信息通过时,会将其保存到缓冲区中。当其他用户再访问相同的信息时,直接从缓冲区中取出信息传给用户,从而提高访问速度。
- 隐藏真实 IP:上网者可以通过代理服务器隐藏自己的 IP,免受攻击。
- 代码配置代理:
- 创建
Request
对象。 - 创建
ProxyHandler
对象,并传入代理服务器的地址信息(以字典形式)。 - 用
handler
对象创建opener
对象。 - 使用
opener.open
函数发送请求。
- 创建
以下是使用代理服务器访问百度并查询 IP 的代码示例:
import urllib.request
import urllib.parse
import urllib.error
import jsonurl = 'http://www.baidu.com/s?wd=ip'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
}
request = urllib.request.Request(url, headers=headers)# 采用代理,以 key-value 的形式(快代理等网站)
proxies = {"http": "103.25.36.27:1518"
}handler = urllib.request.ProxyHandler(proxies=proxies)opener = urllib.request.build_opener(handler)resource = opener.open(request)html = resource.read().decode('utf-8')
with open('ip.html', 'w', encoding='utf-8') as f:f.write(html)
16、随机选择代理
为了进一步提高爬虫的稳定性和隐蔽性,可以随机选择代理服务器。以下是随机选择代理并访问百度查询 IP 的代码:
import urllib.request
import urllib.parse
import randomproxies = [{"http": "103.215.36.27:15818"},
]proxy = random.choice(proxies)
print(proxy)url = 'http://www.baidu.com/s?wd=ip'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0'
}
request = urllib.request.Request(url, headers=headers)
handler = urllib.request.ProxyHandler(proxies=random.choice(proxies))
opener = urllib.request.build_opener(handler)
response = opener.open(request)
html = response.read().decode('utf-8')
# print(html)with open('ip.html', 'w', encoding="utf-8") as f:f.write(html)
在上述代码中,我们首先定义了一个包含多个代理服务器地址的列表,然后通过random.choice
方法随机选择一个代理服务器。接着,按照使用代理服务器的步骤创建Request
对象、ProxyHandler
对象、opener
对象,并发送请求获取响应数据。
四、关键要点深入分析
- 合法合规:
- 在进行爬虫开发时,一定要遵守法律法规和网站的使用条款。不得爬取受版权保护的内容、个人隐私信息等敏感数据。例如,一些金融数据、医疗数据等通常受到严格的法律保护,未经授权的爬取可能会面临严重的法律后果。
- 同时,也要注意尊重网站的 robots.txt 文件,这个文件通常规定了哪些页面可以被爬虫访问,哪些不可以。遵守这些规则可以避免与网站所有者产生纠纷。
- 请求频率:
- 不要过于频繁地向服务器发送请求,以免对服务器造成过大的负担,甚至被服务器封禁 IP。一些热门网站可能会有严格的请求频率限制,如果超过这个限制,可能会被视为恶意攻击。
- 可以通过设置随机等待时间、使用代理 IP 等方式来控制请求频率。例如,在每次请求之间随机等待一段时间,这样可以模拟人类的浏览行为,减少被服务器识别为爬虫的可能性。同时,使用代理 IP 可以轮换 IP 地址,避免单个 IP 被封禁。
- 数据解析:
- 选择合适的数据解析方法非常重要。可以根据网页的结构和数据的特点,选择使用正则表达式、XPath、BeautifulSoup 等库来解析数据。
- 正则表达式是一种强大的文本匹配工具,但对于复杂的 HTML 结构,使用正则表达式可能会变得非常复杂和难以维护。XPath 和 BeautifulSoup 则更加适合解析 HTML 和 XML 文档,可以通过简洁的语法快速定位到所需的数据。
- 例如,如果要从一个 HTML 页面中提取所有的链接,可以使用 BeautifulSoup 的
find_all('a')
方法,非常方便快捷。
- 异常处理:
- 在爬虫开发过程中,可能会遇到各种异常情况,如网络连接失败、页面解析错误等。因此,我们需要进行充分的异常处理,以保证程序的稳定性和可靠性。
- 可以使用 Python 的异常处理机制,如
try...except
语句,来捕获和处理各种异常。例如,当网络连接失败时,可以捕获urllib.error.URLError
异常,并进行相应的处理,如等待一段时间后重新尝试连接。
五、Python 爬虫的应用场景
- 数据分析:通过爬取大量的数据,可以进行数据分析和挖掘,发现有价值的信息和趋势。例如,爬取电商网站的商品信息和用户评价,可以进行市场调研和竞品分析。
- 信息监测:可以实时监测特定网站或主题的信息变化,及时获取最新的消息和动态。例如,监测新闻网站的头条新闻,可以及时了解时事热点。
- 学术研究:对于学术研究人员来说,爬虫可以帮助他们收集大量的研究数据,如学术论文、科研成果等。
六、总结
Python 爬虫是一种非常强大的数据获取工具,但在使用过程中需要注意合法合规、请求频率、数据解析和异常处理等关键要点。只有这样,我们才能充分发挥 Python 爬虫的优势,为我们的数据分析和业务决策提供有力支持。同时,也要不断学习和掌握新的技术和方法,以应对不断变化的网络环境和数据需求。