背景故事/问题
我有一个运行各种作业的 Web 应用程序。其中一项工作包括运行登录到LinkedIn的 python 脚本。
当我运行脚本时,无头 chromium 实例启动,导航到LinkedIn登录页面并提交正确的凭据。到目前为止一切正常,但有时LinkedIn会将我的请求发送到检查点 url,并提示我提交发送到我的电子邮件的特殊密码,然后才能继续。
在我看来,我很难理解如何实现这个条件逻辑;基本上,我希望有一种可以在我的 Web 应用程序中输入密码的方法。
预期成果
从 django 视图运行脚本以登录到LinkedIn,并在到达检查点时提示用户输入 PIN 码,否则如果未到达检查点且登录成功,则继续。
我尝试过什么
-
谷歌 - 找不到任何特定于我的问题。
-
下面的代码 - 下面的视图显示了我当前的尝试。我知道这是可以做到的,但我不确定我是否应该尝试在单个视图中构建它,或者我是否应该尝试将我的 chromium 实例传递给另一个视图(对象不可序列化,所以我不确定我将如何做到这一点;也许我可以将 chromium id 传递给我的 Web 驱动程序的另一个实例, 但我不认为这是最好的方法?
-
阿贾克斯?——我还没有真正尝试过这个,但我知道这是一个选择。
笔记**
下面包含的代码 Ive 就是一切。但唯一要考虑的重要部分是视图,我认为添加其余部分可能会澄清逻辑并帮助某人帮助我。希望这不会太啰嗦!
视图
def verify_linkedin(request):
"""Used for verifying login to linkedin with email pincode"""
ln = LinkedinScraper()
if ln.login_to_linkedin(username=os.getenv("LINKEDIN_USERNAME"), password=os.getenv("LINKEDIN_PASSWORD")):
return HttpResponse("<h1>You are verified</h1>")
else:
if request.method == "POST":
form = VerifyLinkedinForm(request.POST)
if form.is_valid():
print(form.cleaned_data)
pin_code = form['verification_key']
ln.authenticate_linkedin_login(pin_code)
return redirect("home")
else:
form = VerifyLinkedinForm()
context = {"form": form}
return render(request, "linkedin_verify.html", context)
形式
class VerifyLinkedinForm(forms.Form):
verification_key = forms.CharField()
helper = FormHelper()
helper.form_method = "post"
helper.layout = Layout(
'verification_key',
FormActions(
Submit("submit", "Verify Linkedin", style="display: block; margin: auto; margin-bottom:2em;",
css_class="btn btn-success", ),
)
)
模板
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% crispy form %}
{% endblock %}
网络爬虫后端
class LinkedinScraper(WebScraper):
"""Class for logging into linkedin with chromedriver and scraping linkedin"""
def __init__(self, headless=True, **kwargs):
super(LinkedinScraper, self).__init__()
self.setup_driver(headless=headless)
self.data = {}
self.pause = 10
self.retries = 0
self.max_retries = 3
def login_to_linkedin(self, username: str, password: str) -> bool:
""" Navigates to Linkedin login page and logs in with credentials"""
login_url = "https://www.linkedin.com/login"
self.driver.get(login_url)
user_element = self.driver.find_element("id", "username")
pass_element = self.driver.find_element("id", "password")
login_btn = self.driver.find_element_by_class_name(
"login__form_action_container"
)
self.login_and_authenticate(login_btn, user_element, pass_element, username, password)
if 'checkpoint' in self.driver.current_url:
return False
else:
return True
def authenticate_linkedin_login(self, pin_code: str):
""" Sometimes when logging into linkedin from a new ip address, linkedin will flag account for suscipicous access
If this happens we need to use a pin code sent to email and input it into the form.
This should be immedidately called after login_and_authenticate returns False
"""
if 'checkpoint' in self.driver.current_url:
verification_element = self.driver.find_element_by_xpath("//input[@id='input__email_verification_pin']")
print(pin_code)
self.random_send_keys(element=verification_element, keys=pin_code)
submit_element = self.driver.find_element("id", "email-pin-submit-button")
submit_element.click()
return True
else:
raise BaseException(f"Driver not on correct url, current url is {self.driver.current_url} -- Should contain checkpoint")
有两种方法可以解决这个问题。 一种方法是在视图之外创建对象实例,这有点黑客,完全不可扩展,但我能够通过 webrowser 使用 Selenium 验证我的电子邮件帐户。我不必在视图之间传递对象,因为它是全局的。
第二种方法是不使用硒。这就是我最终所做的。 如果有人对这种方法感兴趣,请给我留言或查看pyppeteer。