web-scraping-login-python

如何使用Python抓取需要登录的网站

在网络抓取时,您可能会发现一些数据只有在您登录后才可用。在本教程中,我们将学习使用的安全措施和三种有效的方法来抓取需要使用Python 登录的网站。

你能抓取需要登录的网站吗?

是的,在登录后进行网络抓取在技术上是可行的。但是您必须注意目标站点的抓取规则以及 GDPR 等遵守个人数据和隐私事宜的法律。

开始之前,必须了解一些有关HTTP 请求方法的一般知识。如果您不熟悉网络抓取,我们建议您阅读我们的Python 网络抓取指南以掌握基础知识。

如何使用 Python 登录网站?

抓取需要使用 Python 登录的网站的第一步是弄清楚目标域使用的登录类型。一些旧网站只需要发送用户名和密码。然而,现代网站使用更先进的安全措施。他们包括:

  • 客户端验证。
  • CSRF 令牌。
  • Web 应用程序防火墙 (WAF)。

继续阅读以学习绕过这些严格安全保护的技术。

您如何在 Python 登录后抓取网站?

我们将看到使用 Python 逐步抓取站点登录背后的数据。我们将从只需要用户名和密码的表单开始,然后逐渐增加难度。

本教程中展示的方法仅用于教育目的。

需要简单用户名和密码登录的站点

我们假设您已经设置了 Python 3 和 Pip,否则您应该查看有关正确安装 Python 的指南。

作为依赖项,我们将使用RequestsBeautifulSoup库。从安装它们开始:

pip install requests beautifulsoup4

提示:如果您在安装过程中遇到任何问题,请访问此页面获取 Requests,并访问此页面获取 Beautiful Soup

现在,转到Acunetix 的用户信息。这是一个专门为学习目的而制作的测试页面,受简单登录保护,因此您将被重定向到登录页面

在继续之前,我们将分析尝试登录时发生的情况。为此,使用test用户名和密码,点击登录按钮并检查浏览器上的网络部分。

simple_login

提交表单会生成POST用户信息页面的请求,服务器会用 cookie 进行响应并完成请求的部分。下面的屏幕截图显示了标头、负载、响应和 cookie

post_request_reponse

以下网络抓取脚本将绕过登录。它创建一个类似的负载并将请求发布到用户信息页面。响应到达后,程序使用 Beautiful Soup 解析响应文本并打印页面名称。

from bs4 import BeautifulSoup as bs 
import requests 
URL = "http://testphp.vulnweb.com/userinfo.php" 
 
payload = { 
    "uname": "test", 
    "pass": "test" 
} 
s = requests.session() 
response = s.post(URL, data=payload) 
print(response.status_code) # If the request went Ok we usually get a 200 status. 
 
from bs4 import BeautifulSoup 
soup = BeautifulSoup(response.content, "html.parser") 
protected_content = soup.find(attrs={"id": "pageName"}).text 
print(protected_content)

这是我们的输出:

output_simple_login

伟大的!🎉 您刚刚学习了使用 Python 在简单登录后抓取网站。现在,让我们尝试使用更复杂的保护措施。

使用 CSRF 令牌身份验证登录网站

2023年,登录网站不是那么容易的。大多数网站都实施了额外的安全措施来阻止黑客和恶意机器人。其中一项措施需要在身份验证过程中使用 CSRF(跨站点请求伪造)令牌。

要了解您的目标网站是否需要 CSRF 或authenticity_token,请充分利用浏览器的开发人员工具。无论您使用 Safari、Chrome、Edge、Chromium 还是 Firefox 都没有关系,因为它们都为开发人员提供了一组相似的强大工具。要了解更多信息,我们建议查看Chrome DevToolsMozilla DevTools文档。

让我们开始抓取 GitHub!

第 1 步:登录 GitHub 帐户

GitHub 是使用 CSRF 令牌身份验证进行登录的网站之一。我们将抓取测试帐户中的所有存储库以进行演示。

打开网络浏览器(在我们的例子中是 Chrome)并导航到GitHub 的登录页面。现在,按下F12键在浏览器中查看 DevTools 窗口并检查页面的 HTML 以检查登录表单元素是否具有 action 属性:

git_login_inspect

Network从 DevTools 窗口中选择选项卡并单击Sign in按钮,然后自行填写并提交表单。这将执行几个 HTTP 请求,在此选项卡中可见。

git_login_page

让我们通过查看刚刚发送的命名请求来查看单击“登录”按钮后得到的内容。POSTsession

在该Headers部分中,您将找到发布登录凭据的完整 URL。我们将使用它在我们的脚本中发送登录请求。

git_login_request

第 2 步:为受 CSRF 保护的登录请求设置有效负载

现在,您可能想知道我们如何知道存在 CSRF 保护。答案就在我们面前:

导航到请求Payload的部分session。请注意,除了login和之外password,我们还有身份验证令牌和时间戳的有效负载数据。此真实性令牌是 CSRF 令牌,必须作为有效负载随登录POST请求传递。

git_login_required_fields

从每个新登录请求的部分手动复制这些字段Payload非常繁琐。我们肯定会编写代码以编程方式获取它。

接下来,再次查看登录表单的 HTML 源代码。您会看到所有Payload字段都出现在表单中。

git_login_form_html

以下脚本从登录页面获取CSRF token,timestamp和:timestamp_secret

import requests 
from bs4 import BeautifulSoup 
login_url = "https://github.com/session" 
login = "Your Git username Here" 
password = "Your Git Password Here" 
with requests.session() as s: 
    req = s.get(login_url).text 
    html = BeautifulSoup(req,"html.parser") 
    token = html.find("input", {"name": "authenticity_token"}). attrs["value"] 
    time = html.find("input", {"name": "timestamp"}).attrs["value"] 
    timeSecret = html.find("input", {"name": "timestamp_secret"}). attrs["value"]

我们现在可以payload为我们的 Python 登录请求填充我们的字典:

payload = { 
    "authenticity_token": token, 
    "login": login, 
    "password": password, 
    "timestamp": time, 
    "timestamp_secret": timeSecret 
}

注意:如果您在 HTML 中找不到 CSRF 令牌,它可能保存在 cookie 中。在基于 Chromium 的浏览器(如 Chrome)中,从 DevTools 转到选项Application卡。然后,在左侧面板中,搜索cookies并选择目标网站的域。

cookies

第 3 步:设置标题

可以通过简单地发送请求来访问需要登录的POST网站payload。然而,单独使用这种方法来抓取具有高级安全措施的网站是天真的,因为它们通常足够聪明,可以识别非人类行为。因此,可能有必要采取措施使爬虫看起来比机器人更人性化。

最基本和最现实的方法是将真实的浏览器标头添加到我们的请求中。从浏览器请求的选项卡中复制标头Headers,并将其添加到 Python 登录请求中。您可能需要了解有关请求标头设置的更多信息。

或者,您可以使用网络抓取 API来为您绕过大量烦人的反机器人系统。

第 4 步:登录操作

这是我们的幸运日,因为您不需要为 GitHub 添加标头,所以我们已准备好通过 Python 发送登录请求:

res = s.post(login_url, data=payload) 
print(res.url)

如果登录成功,则输出为https://github.com/https://github.com/session否则为。

👍 太棒了,我们刚刚确定了一个受 CSRF 保护的登录绕过!现在让我们抓取受保护的 git 存储库中的数据。

第 5 步:抓取受保护的 GitHub 存储库

回想一下,我们在较早的代码中使用with requests.session() as s:创建请求会话的语句开始。通过会话中的请求登录后,您无需为同一会话中的后续请求重新登录。

是时候访问存储库了。生成一个GET,然后使用 BeautifulSoup 解析响应。

repos_url = "https://github.com/" + login + "/?tab=repositories" 
r = s.get(repos_url) 
soup = BeautifulSoup(r.content, "html.parser")

我们将提取用户名和存储库列表。

首先,对于用户名,导航到浏览器中的存储库页面,然后右键单击用户名并选择Inspect Element。用户名包含在 span 元素中,CSS 类在标签p-nickname vcard-username d-block内命名<h1>

git_username_source

其次,对于存储库,右键单击任何存储库名称并选择Inspect Element. DevTools 窗口将显示以下内容:

repos_html_source

存储库的名称位于<h3>带有 class 的标签中的超链接内wb-break-all。好的,我们现在对目标元素有了足够的了解,所以让我们提取它们:

usernameDiv = soup.find("span", class_="p-nickname vcard-username d-block") 
print("Username: " + usernameDiv.getText()) 
repos = soup.find_all("h3",class_="wb-break-all") 
for r in repos: 
    repoName = r.find("a").getText() 
    print("Repository Name: " + repoName)

由于可以在目标网页上找到多个存储库,因此脚本使用该find_all()方法提取所有。为此,循环遍历每个<h3>标签并打印所附标签的文本<a>

完整代码如下所示:

import requests 
from bs4 import BeautifulSoup 
 
login = "Your Username Here" 
password = "Your Password Here" 
login_url = "https://github.com/session" 
repos_url = "https://github.com/" + login + "/?tab=repositories" 
 
with requests.session() as s: 
    req = s.get(login_url).text 
    html = BeautifulSoup(req,"html.parser") 
    token = html.find("input", {"name": "authenticity_token"}).attrs["value"] 
    time = html.find("input", {"name": "timestamp"}).attrs["value"] 
    timeSecret = html.find("input", {"name": "timestamp_secret"}).attrs["value"] 
 
    payload = { 
        "authenticity_token": token, 
        "login": login, 
        "password": password, 
        "timestamp": time, 
        "timestamp_secret": timeSecret 
    } 
    res =s.post(login_url, data=payload) 
 
    r = s.get(repos_url) 
    soup = BeautifulSoup (r.content, "html.parser") 
    usernameDiv = soup.find("span", class_="p-nickname vcard-username d-block") 
    print("Username: " + usernameDiv.getText()) 
 
    repos = soup.find_all("h3", class_="wb-break-all") 
    for r in repos: 
        repoName = r.find("a").getText() 
        print("Repository Name: " + repoName)

输出:

repos_output

我们刚刚抓取了一个 CSRF 认证的网站。

在受 WAF 保护的网站上抓取登录信息

在许多网站上,在发送正确的用户、密码和 CSRF 令牌后,您仍然会进入拒绝访问屏幕或收到 403 等 HTTP 错误。即使使用正确的请求标头也行不通。这表明该网站使用了高级保护措施,例如客户端浏览器验证。

客户端验证是一种阻止机器人和爬虫访问网站的安全措施,主要由 WAF(Web 应用程序防火墙)实施,如CloudflareAkamaiPerimeterX

让我们看看如何找到解决方案。

使用 Selenium 的基本 WAF 保护

如果您仅使用 Requests 和 BeautifulSoup 库来处理需要类似人类交互的登录,那么被阻止的风险就太高了。替代方案?无头浏览器。它们是您所知道的标准浏览器,例如 Chrome 或 Firefox,但它们没有任何可供人类用户交互的 GUI。它们的美妙之处在于它们可以通过编程方式进行控制。

人们发现诸如 Selenium 之类的无头浏览器可以很好地绕过 WAF 的基本登录保护。此外,它们使您能够登录在登录过程中使用两步验证(您输入电子邮件,然后出现密码字段)的网站,例如 Twitter。

Selenium 有一组工具可以帮助您创建无头浏览器实例并使用代码控制它。尽管基本的 Selenium 实现不足以抓取受 WAF 保护的站点,但可以使用一些扩展库来帮助我们实现此目的。undetected-chromedriver是一个不可检测的 ChromeDriver 自动化库,它使用多种规避技术来避免检测。我们将在本教程中进行。

我们这个案例的目标站点是 DataCamp,这是一个面向数据分析爱好者的电子学习网站,它有一个两步登录。我们会这样做:

  1. 在DataCamp上创建一个帐户并注册 Python 课程,以便接下来抓取我们的数据。
  2. 使用登录到 DataCamp undetected-chromedriver
  3. 导航和抓取https://app.datacamp.com/learn
  4. 从解析的 HTML 中提取个人资料名称和注册课程。

让我们从安装和导入所需的模块和库开始。

pip install selenium undetected-chromedriver
import undetected_chromedriver as uc 
import time 
from selenium.webdriver.common.by import By

现在,使用该对象创建一个不可检测的无头浏览器实例uc并移至登录页面。

chromeOptions = uc.ChromeOptions() 
chromeOptions.headless = true 
driver = uc.Chrome(use_subprocess=True, options=chromeOptions) 
driver.get("https://www.datacamp.com/users/sign_in")

要以编程方式输入电子邮件和密码字段,您需要id从登录表单中获取输入字段的 。为此,请在浏览器中打开登录页面并右键单击电子邮件字段以检查该元素。这将在 DevTools 窗口中打开相应的 HTML 代码。

以下屏幕截图显示了电子邮件字段的 HTML 源代码,这是我们需要的第一个:

datacamp

由于登录遵循两步过程,我们最初只有Email address表单上的字段带有id="user_email". 让我们以编程方式填充它并单击Next按钮。

uname = driver.find_element(By.ID, "user_email") 
uname.send_keys("Your Email Here") 
driver.find_element(By.CSS_SELECTOR, ".js-account-check-email").click() 
time.sleep(10)

请注意,添加 10 秒的休眠是为了让 JavaScript 动态加载该Password字段。

以下代码输入密码并点击提交按钮请求登录:

passwordF = driver.find_element(By.ID, "user_password") 
passwordF.send_keys("Your Password Here") 
driver.find_element(By.NAME, "commit").click()

您已登录。

无头实例成功登录后,您可以转到仪表板中可用的任何网页。由于我们想从仪表板页面抓取个人资料名称和注册课程,我们将在以下屏幕截图所示的位置找到它们:

datacamp_learn_page

下面的代码将检索并解析目标 URL 以显示个人资料名称和注册课程。

driver.get("https://app.datacamp.com/learn") 
myName = driver.find_element(By.CLASS_NAME, "mfe-app-learn-hub-15alavv") 
myCourse = driver.find_element(By.CLASS_NAME, "mfe-app-learn-hub-1f1m67o") 
 
print("Profile Name: " + myName.get_attribute("innerHTML")) 
print("Course Enrolled: " + myCourse.get_attribute("innerHTML")) 
driver.close()

让我们结合之前的所有代码块,看看完整的抓取脚本是什么样子的。

import undetected_chromedriver as udc 
import time 
from selenium.webdriver.common.by import By 
username="Your Username Here"; 
password="Your Password Here" 
chromeOptions = udc.ChromeOptions() 
chromeOptions.headless = True 
driver = udc.Chrome(use_subprocess=True, options=chromeOptions) 
driver.get("https://www.datacamp.com/users/sign_in") 
uname = driver.find_element(By.ID, "user_email") 
uname.send_keys(username) 
driver.find_element(By.CSS_SELECTOR, ".js-account-check-email").click() 
 
time.sleep(5) 
passwordF = driver.find_element(By.ID, "user_password") 
passwordF.send_keys(password) 
driver.find_element(By.NAME, "commit").click() 
time.sleep(2) 
driver.get("https://app.datacamp.com/learn") 
time.sleep(2) 
myName = driver.find_element(By.CLASS_NAME, "mfe-app-learn-hub-15alavv") 
myCourse = driver.find_element(By.CLASS_NAME, "mfe-app-learn-hub-1f1m67o") 
 
print("Profile Name: " + myName.get_attribute("innerHTML")) 
print("Course Enrolled: " + myCourse.get_attribute("innerHTML")) 
driver.close()

我们建议更改headless选项以False了解后面发生了什么。根据您的个人资料名称和注册课程,输出应如下所示:

output_two_step_login

我们刚刚抓取了受 WAF 保护的登录背后的内容。但是每个网站都一样吗?

目前,该undetected-chromedriver软件包仅支持 109 或更高版本的 Chromium 浏览器。此外,受 WAF 保护的站点可以轻松检测到其无头模式。

抓取需要使用 Python 登录的网站,undetected-chromedriver如果保护是基本的,可能就足够了。但我们假设该站点使用高级 Cloudflare 保护(例如G2)或其他 DDoS 缓解服务。在这种情况下,我们看到的解决方案可能并不可靠。

使用 ZenRows 的高级保护

在具有更高保护措施的网站上抓取登录后的内容需要正确的工具。为此,我们将使用ZenRows API。

我们的任务包括绕过G2.com 的登录页面,这是两步登录的第一步,然后在我们登录后从主页提取欢迎消息。

但在开始编写代码之前,让我们首先使用 DevTools 探索我们的目标。下表列出了有关我们将在整个脚本中与之交互的 HTML 元素的必要信息。请记住接下来的步骤。

Element/Purpose Element Type Attribute Value
G2 login (step 1): Email input <input type=”email”> Class input-group-field
G2 login (step 1): Next button to proceed to the next login step <button> Class js-button-submit
G2 login (Step 2): Password Field <input type=”password”> Id password_input
G2 login (Step 2): Login form submit button <input type=”submit”> CSS Selector input[value='Sign In']
Welcome message at Homepage <div> Class l4 color-white my-1
[/su_table]

使用 ZenRows,您不需要安装任何特定的浏览器驱动程序(就像使用 Selenium 一样)。此外,您无需担心高级 Cloudflare 保护、身份泄露和其他 DDoS 缓解服务。此外,这个可扩展的 API 使您免于基础架构可扩展性问题。

只需免费注册即可访问 Request Builder 并按照下面的屏幕截图填写详细信息。

zenrows_2step_scraping

下面一步步说说请求的创建:

  1. 设置初始目标(即在我们的案例中为G2 登录页面)。
  2. 选择纯 H​​TML。我们稍后将在代码中使用 BeatifulSoup 进一步解析它。如果您愿意,可以使用CSS Selectors仅从目标中抓取一些特定元素。
  3. 设置高级代理可帮助您抓取特定区域的数据并保护您免受身份泄露。
  4. 设置JavaScript 呈现对于在第 6 步中运行某些 JavaScript 指令是强制性的。
  5. 选择Antibot可帮助您绕过高级 WAF 安全措施。
  6. 检查JavaScript 指令允许您添加编码的JavaScript 指令字符串以在目标上运行。它允许类似于无头浏览器的控件。
  7. 当您选中 JavaScript Instructions 复选框时,将出现一个文本框。您可以编写任意数量的 JS 指令,我们放置以下指令:在我们的例子中:
[ 
    {"wait": 2000}, 
    {"evaluate": "document.querySelector('.input-group-field').value = 'Your Business Email Here';"}, 
    {"wait": 1000}, 
    {"click": ".js-button-submit"}, 
    {"wait": 2000}, 
    {"evaluate": "document.querySelector('#password_input').value = 'Your Password Here';"}, 
    {"wait": 1000}, 
    {"click": "input[value='Sign In']"}, 
    {"wait": 6000} 
]

注意:通过添加您自己的登录凭据来更新上面的代码。

  1. 选择Python
  2. 选择 SDK 并复制整个代码。请记住使用安装 ZenRows SDK 包pip install zenrows

现在,您可以将此代码粘贴到您的 Python 项目中并执行它。我们复制了 SDK 代码并对其进行了修改,使其更易于移植和更易于理解。

# pip install zenrows 
from zenrows import ZenRowsClient 
import urllib 
import json 
 
client = ZenRowsClient("Your Zenrows API Goes Here") 
url = "https://www.g2.com/login?form=signup#state.email.showform" 
 
js_instructions = [ 
    {"wait": 2000}, 
    {"evaluate": "document.querySelector('.input-group-field').value = 'Your G2 Login Email Here';"}, 
    {"wait": 1000}, 
    {"click": ".js-button-submit"}, 
    {"wait": 2000}, 
    {"evaluate": "document.querySelector('#password_input').value = 'Your G2 Password Here';"}, 
    {"wait": 1000}, 
    {"click": "input[value='Sign In']"}, 
    {"wait": 6000} 
] 
 
params = { 
    "js_render":"true", 
    "antibot":"true", 
    "js_instructions":urllib.parse.quote(json.dumps(js_instructions)), 
    "premium_proxy":"true" 
} 
 
response = client.get(url, params=params) 
 
print(response.text)

该代码片段在登录后从G2 主页获取并打印纯 HTML 。现在,我们将使用BeatifulSoup 进一步解析 HTML 并提取我们想要的数据。

from bs4 import BeautifulSoup 
soup = BeautifulSoup(response.text, "html.parser") 
welcome = soup.find("div", attrs={"class", "l4 color-white my-1"}) 
print(welcome.text)

output_zenrows_two_step_login

这是完整的代码:

# pip install zenrows 
from zenrows import ZenRowsClient 
from bs4 import BeautifulSoup 
import urllib 
import json 
 
client = ZenRowsClient("Your Zenrows API Goes Here") 
url = "https://www.g2.com/login?form=signup#state.email.showform" 
 
js_instructions = [ 
    {"wait": 2000}, 
    {"evaluate": "document.querySelector('.input-group-field').value = 'Your G2 Login Email Here';"}, 
    {"wait": 1000}, 
    {"click": ".js-button-submit"}, 
    {"wait": 2000}, 
    {"evaluate": "document.querySelector('#password_input').value = 'Your G2 Password Here';"}, 
    {"wait": 1000}, 
    {"click": "input[value='Sign In']"}, 
    {"wait": 6000} 
] 
 
params = { 
    "js_render":"true", 
    "antibot":"true", 
    "js_instructions":urllib.parse.quote(json.dumps(js_instructions)), 
    "premium_proxy":"true" 
} 
 
response = client.get(url, params=params) 
 
soup = BeautifulSoup(response.text, "html.parser") 
welcome = soup.find("div", attrs={"class", "l4 color-white my-1"}) 
print(welcome.text)

结论

什么可以抓取需要使用 Python 登录的网站?正如所见,使用 BeautifulSoup 检查 HTML 并使用 Requests 库获取 cookie 可以帮助您。但是,对于具有强大反机器人解决方案的现代网站,您需要无法检测到的无头浏览器。它们的问题是可扩展性、成本和性能限制。此外,它们仍然可能被实施了高级 WAF 的网站阻止。

类似文章