Saltar al contenido

Cómo raspar un sitio web que requiere iniciar sesión primero con Python

Solución:

Esto funciona para mi:

##################################### Method 1
import mechanize
import cookielib
from BeautifulSoup import BeautifulSoup
import html2text

# Browser
br = mechanize.Browser()

# Cookie Jar
cj = cookielib.LWPCookieJar()
br.set_cookiejar(cj)

# Browser options
br.set_handle_equiv(True)
br.set_handle_gzip(True)
br.set_handle_redirect(True)
br.set_handle_referer(True)
br.set_handle_robots(False)
br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)

br.addheaders = [('User-agent', 'Chrome')]

# The site we will navigate into, handling it's session
br.open('https://github.com/login')

# View available forms
for f in br.forms():
    print f

# Select the second (index one) form (the first form is a search query box)
br.select_form(nr=1)

# User credentials
br.form['login'] = 'mylogin'
br.form['password'] = 'mypass'

# Login
br.submit()

print(br.open('https://github.com/settings/emails').read())

¡No estabas lejos del todo!

Me encantaría agregar mi solución al lado. esta respuesta sigue principalmente el enfoque hacky / lazy que siempre sigo en todo lo que hago. Continué principalmente porque era demasiado vago para manejar las cookies, los datos de la sesión, etc.

esta solución es de mayor utilidad si desea raspar varias páginas de un sitio web después de iniciar sesión con credenciales de una sola cuenta (por ejemplo, todos sus tableros de pinterest). no si quieres automatizar la autenticación usando múltiples cuentas

entonces mi solución es el selenio junto con los perfiles de Firefox.

  • Crea un nuevo perfil de firefox crea un nuevo perfil de Firefox, observe la ubicación donde está almacenado, abra Firefox en el perfil correspondiente. e inicie sesión en el sitio web manualmente. detalles sobre los perfiles de firefox
  • ahora usa selenio con este perfil La sesión de selenium utilizará las cookies y los datos de sesión del perfil de Firefox para que su autenticación se mantenga.

Ideé este mecanismo cuando encontré la necesidad de raspar algunas páginas de Pinterest, agregué algunas líneas de código de la muestra que muestran cómo usar el perfil. adaptarse al código de acuerdo a sus necesidades.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException


#replace with your firefox profile
fp=webdriver.FirefoxProfile('C:/Users/SJ/AppData/Roaming/Mozilla/Firefox/Profiles/hlsfrs2o.scrape')
#enter your url here
url=""
driver = webdriver.Firefox(fp)
driver.get(url)

html_source = driver.page_source

La forma clásica de abordar este problema es:

  1. inicie un navegador, vaya al sitio y busque la página de inicio de sesión
  2. inspeccione el código fuente de la página para averiguar: I. cuál es el formulario de inicio de sesión (una página puede tener muchos formularios, pero normalmente uno de ellos es el formulario de inicio de sesión) II. cuáles son los nombres de campo utilizados para el nombre de usuario y la contraseña (estos pueden variar mucho) III. si hay otros campos que deben enviarse (como un token de autenticación)
  3. escriba la araña Scrapy para replicar el envío del formulario usando FormRequest

Siendo fanáticos de la automatización, pensamos que podríamos escribir algún código para automatizar el punto 2 (que en realidad es el que lleva más tiempo) y el resultado es un formulario de inicio de sesión, una biblioteca para completar automáticamente los formularios de inicio de sesión dada la página de inicio de sesión, el nombre de usuario y la contraseña. Aquí está el código de una araña simple que usaría el formulario de inicio de sesión para iniciar sesión en los sitios automáticamente.

githubloginspider.py

from scrapy.spider import BaseSpider
from scrapy.http import FormRequest
from scrapy.http.request import Request
from loginform import fill_login_form
from scrapy import log
from scraping.articles import ArticleItem


class GitHubLogin(BaseSpider):

    name = 'GitHubLogin'
    allowed_domains = ['github.com']
    start_urls = ['http://github.com/login']
    login_user = 'ranvijay5686'
    login_pass = ''

    def parse(self, response):
        (args, url, method) = fill_login_form(response.url,
                response.body, self.login_user, self.login_pass)
        return FormRequest(url, method=method, formdata=args,
                           callback=self.after_login)

    def after_login(self, response):

        # for link in response.xpath("//*[@id='site-container']/div[2]/div[4]/p/a/@href").extract():

        item = ArticleItem()
        item['title'] = 'ranvijay'
        log.msg('***************    :   '
                + str(response.xpath("//form[@class='subnav-search left']/input/@value"
                ).extract()))
        item['url'] = 
            response.xpath("//*[@id='site-container']/div[1]/div/div/span/span/text()"
                           ).extract()
        yield item

items.py

from scrapy.item import Item, Field

class ArticleItem(Item):
    title = Field()
    url = Field()

loginform.py

import sys
from argparse import ArgumentParser
from collections import defaultdict
from lxml import html

__version__ = '1.0'  # also update setup.py


def _form_score(form):
    score = 0

    # In case of user/pass or user/pass/remember-me

    if len(form.inputs.keys()) in (2, 3):
        score += 10

    typecount = defaultdict(int)
    for x in form.inputs:
        type_ = (x.type if isinstance(x, html.InputElement) else 'other'
                 )
        typecount[type_] += 1

    if typecount['text'] > 1:
        score += 10
    if not typecount['text']:
        score -= 10

    if typecount['password'] == 1:
        score += 10
    if not typecount['password']:
        score -= 10

    if typecount['checkbox'] > 1:
        score -= 10
    if typecount['radio']:
        score -= 10

    return score


def _pick_form(forms):
    """Return the form most likely to be a login form"""

    return sorted(forms, key=_form_score, reverse=True)[0]


def _pick_fields(form):
    """Return the most likely field names for username and password"""

    userfield = passfield = emailfield = None
    for x in form.inputs:
        if not isinstance(x, html.InputElement):
            continue

        type_ = x.type
        if type_ == 'password' and passfield is None:
            passfield = x.name
        elif type_ == 'text' and userfield is None:
            userfield = x.name
        elif type_ == 'email' and emailfield is None:
            emailfield = x.name

    return (userfield or emailfield, passfield)


def submit_value(form):
    """Returns the value for the submit input, if any"""

    for x in form.inputs:
        if x.type == 'submit' and x.name:
            return [(x.name, x.value)]
    else:
        return []


def fill_login_form(
    url,
    body,
    username,
    password,
    ):
    doc = html.document_fromstring(body, base_url=url)
    form = _pick_form(doc.xpath('//form'))
    (userfield, passfield) = _pick_fields(form)
    form.fields[userfield] = username
    form.fields[passfield] = password
    form_values = form.form_values() + submit_value(form)
    return (form_values, form.action or form.base_url, form.method)


def main():
    ap = ArgumentParser()
    ap.add_argument('-u', '--username', default='username')
    ap.add_argument('-p', '--password', default='secret')
    ap.add_argument('url')
    args = ap.parse_args()

    try:
        import requests
    except ImportError:
        print 'requests library is required to use loginform as a tool'

    r = requests.get(args.url)
    (values, action, method) = fill_login_form(args.url, r.text,
            args.username, args.password)
    print '''url: 0
method: 1
payload:'''.format(action, method)
    for (k, v) in values:
        print '- 0: 1'.format(k, v)


if __name__ == '__main__':
    sys.exit(main())

Acuérdate de que puedes permitirte añadir un criterio objetivo .

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *