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

En primer lugar, creo que vale la pena decir que sé que hay un montón de preguntas similares pero NINGUNA de ellas funciona para mí …

Soy un novato en Python, html y web scraper. Estoy tratando de raspar la información del usuario de un sitio web que necesita iniciar sesión primero. En mis pruebas, utilizo el raspador de mi configuración de correo electrónico de github como ejemplos. La página principal es ‘ https://github.com/login ‘ y la página de destino es ‘ https://github.com/settings/emails ‘

Aquí hay una lista de métodos que he probado

##################################### Method 1 import mechanize import cookielib from BeautifulSoup import BeautifulSoup import html2text br = mechanize.Browser() 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') for f in br.forms(): print f br.select_form(nr=0) # User credentials br.form['login'] = 'myusername' br.form['password'] = 'mypwd' # Login br.submit() br.open('github.com/settings/emails').read() ################ Method 2 import urllib, urllib2, cookielib username = 'myusername' password = 'mypwd' cj = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) login_data = urllib.urlencode({'username' : username, 'j_password' : password}) opener.open('https://github.com/login', login_data) resp = opener.open('https://github.com/settings/emails') print resp.read() ############# Method 3 import urllib opener = urllib.FancyURLopener() print opener.open('http://myusername:mypwd@github.com/settings/emails').read() ########## Method 4 import mechanize import cookielib br = mechanize.Browser() cj = cookielib.LWPCookieJar() br.set_cookiejar(cj) 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.set_debug_http(True) #br.set_debug_redirects(True) #br.set_debug_responses(True) br.addheaders = [('User-agent', 'Chrome')] br.add_password('https://github.com/settings/emails', 'myusername', 'mypwd') br.open('https://github.com/settings/emails') print br.response().read() ############ Methods 5 from requests import session payload = { 'action': 'login', 'username': 'myusername', 'password': 'mypwd' } with session() as c: c.post('https://github.com/login', data=payload) request = c.get('https://github.com/settings/emails') print request.headers print request.text ########### Method 6 import requests from requests.packages.urllib3 import add_stderr_logger import sys from bs4 import BeautifulSoup as bs add_stderr_logger() s = requests.Session() s.headers['User-Agent'] = 'Chrome' username = 'myusername' password = 'mypwd' url = 'https://github.com/login' # after examining the HTML of the website you're trying to log into # set name_form to the name of the form element that contains the name and # set password_form to the name of the form element that will contain the password login = {'login': username, 'password': password} login_response = s.post(url, data=login) for r in login_response.history: if r.status_code == 401: # 401 means authentication failed print 'error!' sys.exit(1) # abort pdf_response = s.get('https://github.com/settings/emails') # Your cookies and headers are automatically included soup = bs(pdf_response.content) 

También he leído algunas discusiones sobre las diferencias entre la autenticación HTTP y las cookies. Todavía ninguno de ellos funcionaba.

Por favor ayuda y cualquier ayuda sería apreciada. Muchas gracias.

Esto funciona para mí:

 ##################################### 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 muy lejos!

Me encantaría añadir mi solución al lado. Esta respuesta sigue principalmente el enfoque hacky / perezoso que siempre sigo en todo lo que hago. continuó principalmente porque era demasiado perezoso para manejar las cookies, los datos de sesión, etc.

Esta solución es de la 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.

así que mi solución es selenium junto con los perfiles de firefox.

  • Cree un nuevo perfil de Firefox , cree un nuevo perfil de Firefox, anote 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 use Selenium con este perfil. La sesión de Selenium usará las cookies y los datos de sesión del perfil de Firefox para que su autenticación permanezca.

ideé este mecanismo cuando encontré la necesidad de raspar algunas páginas interesantes, he agregado algunas líneas de código de la muestra que muestra 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. iniciar un navegador, ir al sitio y buscar 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 generalmente 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 (podrían variar mucho) III. si hay otros campos que deben enviarse (como un token de autenticación)
  3. escriba la araña de 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 consume 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())