在很多情况下,我们需要抓取海量的网络数据来进行数据汇总、市场分析、模型训练…这样的业务场景相当多。不过,当您运行网络爬虫时,可能会遇到这样的问题——短时间内从同一IP和设备向目标网站发送太多请求时,该站点可能会出现验证码,甚至屏蔽您的IP地址以阻止您抓取数据。
那么,抓取网站数据的时候如何规避验证码或者避免被屏蔽呢?我们将在本文使用 Python 通过两种不同的方法来解决此问题:
- 随机更换IP地址
- 更改用户代理(User-Agent)
随机更换IP地址
本方法主要思路是为每个请求提供不同的网络代理。如果您继续使用同一个IP,网站监测到后会屏蔽该地址。要解决这个问题,我们可以频繁的更换IP地址,并为每个请求使用不同的IP地址。这种方式虽然可能会降低数据抓取效率,却能够帮助您避免被目标站点屏蔽。
当然,要达成此目标,您可以使用 tor 浏览器来进行。但这种手动方式显然效率极其低下。这里我们将使用一个名为 torpy
的 Python tor 客户端,该客户端不需要您在系统中下载安装 tor 浏览器。
torpy
库的 GitHub 链接如下:
您可以在终端使用以下命令进行安装:
pip install torpy
假设我们想从以下网站爬取数据:
urls = [ "https://www.google.com", "https://www.facebook.com", "https://www.youtube.com", "https://www.amazon.com", "https://www.reddit.com", "https://www.instagram.com", "https://www.linkedin.com", "https://www.wikipedia.org", "https://www.twitter.com"]
我们将编写一个函数,并为每个URL请求启用一个新会话,然后循环抓取以上所有地址。代码如下:
from torpy.http.requests import TorRequests
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def send_request(url):
with TorRequests() as tor_requests:
with tor_requests.get_session() as sess:
# print the IP address of the proxy
print(sess.get("http://httpbin.org/ip").json())
html_content = sess.get(url, timeout=10).text
# your scraping code here ..
if __name__ == "__main__":
# put some random urls
urls = [
"https://www.google.com",
"https://www.facebook.com",
"https://www.youtube.com",
"https://www.amazon.com",
"https://www.reddit.com",
"https://www.instagram.com",
"https://www.linkedin.com",
"https://www.wikipedia.org",
"https://www.twitter.com",
]
for link in urls:
try:
send_request(link)
except Exception as e:
print(e)
pass
上面代码中,我们通过 https://httpbin.org/ip
验证IP地址,并在第11行代码输出当前会话所属IP地址,方便我们查看每个请求的IP地址。
输出如下:
{'origin': '107.189.7.175'}
{'origin': '185.220.101.162'}
{'origin': '185.220.101.79'}
{'origin': '103.236.201.88'}
{'origin': '185.220.100.242'}
{'origin': '209.141.53.20'}
{'origin': '198.98.62.79'}
{'origin': '184.105.220.24'}
{'origin': '193.218.118.167'}
如您所见,每个请求都使用了不同的IP地址。
更改用户代理(User-Agent)
由于多数网站都会屏蔽来自未知/非标准浏览器的请求,因此,我们通常在每次请求时通过指定用户代理(User-Agent)的形式来传递浏览器信息,如下所示:
import requests
USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.4.10 (KHTML, like Gecko) Version/8.0.4 Safari/600.4.10"
HEADERS = {"User-Agent": USER_AGENT}
html_content = requests.get(url, headers=HEADERS, timeout=40).text
用户代理通常包含应用程序类型、操作系统及相关版本等信息。
如果您通过以上代码持续使用同一用户代理发送请求,目标站点可能会检测到来自同一设备的频繁访问从而进行阻断。为避免此类情况,我们必须为每次请求指定不同的用户代理。您可以从这个网站 找到许多有效的用户代理信息。
然后,我们整理一个用户代理列表,并在每个请求中随机选择一个用户代理。我们先来列出这个用户代理列表:
import random
AGENT_LIST = [
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36",
"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/91.0.4472.114 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0"
]
USER_AGENT = random.choice(AGENT_LIST)
然后使用以下代码为每个请求随机更改IP地址和用户代理:
from email.header import Header
from wsgiref import headers
from torpy.http.requests import TorRequests
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
import random
# https://developers.whatismybrowser.com/useragents/explore/operating_system_name/linux/
# https://developers.whatismybrowser.com/useragents/explore/operating_system_name/mac-os-x/
AGENT_LIST = [
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36",
"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/91.0.4472.114 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:77.0) Gecko/20100101 Firefox/77.0",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/78.0.3904.70 Safari/537.36",
"Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36",
]
def send_request(url, HEADERS):
with TorRequests() as tor_requests:
with tor_requests.get_session() as sess:
# print the IP address of the proxy
print(sess.get("http://httpbin.org/ip").json())
html_content = sess.get(url, headers=HEADERS, timeout=10).text
# your scraping code here ..
print(HEADERS["User-Agent"])
if __name__ == "__main__":
# put some random urls
urls = [
"https://www.google.com",
"https://www.facebook.com",
"https://www.youtube.com",
"https://www.amazon.com",
"https://www.reddit.com",
"https://www.instagram.com",
"https://www.linkedin.com",
"https://www.wikipedia.org",
"https://www.twitter.com",
]
for link in urls:
HEADERS = {"User-Agent": random.choice(AGENT_LIST)}
try:
send_request(link, HEADERS)
except Exception as e:
print(e)
pass
为避免触发网站验证码机制,我们可以在每个请求之前加上 time.sleep()
:
def send_request(url, HEADERS):
with TorRequests() as tor_requests:
with tor_requests.get_session() as sess:
# print the IP address of the proxy
print(sess.get("http://httpbin.org/ip").json())
# pause randomly between 1 to 3 seconds
time.sleep(random.randint(1, 3))
html_content = sess.get(url, headers=HEADERS, timeout=10).text
# your scraping code here ..
print(HEADERS["User-Agent"])
如上所示,我们在第 7 行添加了 time.sleep()
语句,随机选择1-3之间的整数。
本文介绍的方法虽然会降低您抓取数据的速度,但有助于避免被目标网站屏蔽,并能够绕过网站验证码。将此思路应用到一些成熟的开源网络爬虫项目,堪当大任。