urllib2.HTTPError: HTTP Error 403: Prohibido

102

Estoy tratando de automatizar la descarga de datos históricos de acciones usando Python. La URL que estoy intentando abrir responde con un archivo CSV, pero no puedo abrir con urllib2. Intenté cambiar el agente de usuario como se especificó en algunas preguntas antes, incluso intenté aceptar cookies de respuesta, sin suerte. ¿Puedes ayudarme?

Nota: El mismo método funciona para Yahoo Finance.

Código:

import urllib2,cookielib

site= "http://www.nseindia.com/live_market/dynaContent/live_watch/get_quote/getHistoricalData.jsp?symbol=JPASSOCIAT&fromDate=1-JAN-2012&toDate=1-AUG-2012&datePeriod=unselected&hiddDwnld=true"

hdr = {'User-Agent':'Mozilla/5.0'}

req = urllib2.Request(site,headers=hdr)

page = urllib2.urlopen(req)

Error

Archivo "C: \ Python27 \ lib \ urllib2.py", línea 527, en http_error_default raise HTTPError (req.get_full_url (), code, msg, hdrs, fp) urllib2.HTTPError: HTTP Error 403: Forbidden

Gracias por tu ayuda

Kumar
fuente
¿Usas Windows como plataforma?
Denis

Respuestas:

170

Al agregar algunos encabezados más, pude obtener los datos:

import urllib2,cookielib

site= "http://www.nseindia.com/live_market/dynaContent/live_watch/get_quote/getHistoricalData.jsp?symbol=JPASSOCIAT&fromDate=1-JAN-2012&toDate=1-AUG-2012&datePeriod=unselected&hiddDwnld=true"
hdr = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
       'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
       'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
       'Accept-Encoding': 'none',
       'Accept-Language': 'en-US,en;q=0.8',
       'Connection': 'keep-alive'}

req = urllib2.Request(site, headers=hdr)

try:
    page = urllib2.urlopen(req)
except urllib2.HTTPError, e:
    print e.fp.read()

content = page.read()
print content

En realidad, funciona solo con este encabezado adicional:

'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
andreano
fuente
¿Cuál de estos encabezados cree que faltaba en la solicitud original?
1
wirehark mostró que solo se envió el User-Agent, junto con Connection: close, Host: www.nseindia.com, Accept-Encoding: identity
andrean
1
De nada, bueno, lo que realmente hice fue verifiqué la URL de su script en un navegador, y mientras funcionaba allí, simplemente copié todos los encabezados de solicitud que envió el navegador y los agregué aquí, y esa fue la solución.
andrean
1
@Mee, ¿echaste un vistazo a la respuesta a continuación? fue abordado específicamente para Python 3, verifique si funciona para usted ...
andrean
1
intente agregar los otros encabezados (de mi respuesta) también a la solicitud. Aún así, hay muchas otras razones por las que un servidor podría devolver un 403, consulte también las otras respuestas sobre el tema. En cuanto al objetivo, especialmente Google es difícil, un poco difícil de raspar, han implementado muchos métodos para evitar raspar.
andrean
50

Esto funcionará en Python 3

import urllib.request

user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7'

url = "http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers"
headers={'User-Agent':user_agent,} 

request=urllib.request.Request(url,None,headers) #The assembled request
response = urllib.request.urlopen(request)
data = response.read() # The data u need
Eish
fuente
2
Es cierto que algunos sitios (incluida Wikipedia) bloquean cadenas de agentes de usuario que no son del navegador, como "Python-urllib / xy" enviadas por las bibliotecas de Python. Incluso un simple "Mozilla" u "Opera" suele ser suficiente para evitarlo. Esto no se aplica a la pregunta original, por supuesto, pero es útil saberlo.
efotinis
7

El sitio web de NSE ha cambiado y los scripts más antiguos son semi-óptimos para el sitio web actual. Este fragmento puede recopilar detalles diarios de seguridad. Los detalles incluyen símbolo, tipo de valor, cierre anterior, precio de apertura, precio alto, precio bajo, precio medio, cantidad negociada, volumen de negocios, número de transacciones, cantidades entregadas y proporción entregada y negociada en porcentaje. Estos se presentan convenientemente como una lista en forma de diccionario.

Versión de Python 3.X con solicitudes y BeautifulSoup

from requests import get
from csv import DictReader
from bs4 import BeautifulSoup as Soup
from datetime import date
from io import StringIO 

SECURITY_NAME="3MINDIA" # Change this to get quote for another stock
START_DATE= date(2017, 1, 1) # Start date of stock quote data DD-MM-YYYY
END_DATE= date(2017, 9, 14)  # End date of stock quote data DD-MM-YYYY


BASE_URL = "https://www.nseindia.com/products/dynaContent/common/productsSymbolMapping.jsp?symbol={security}&segmentLink=3&symbolCount=1&series=ALL&dateRange=+&fromDate={start_date}&toDate={end_date}&dataType=PRICEVOLUMEDELIVERABLE"




def getquote(symbol, start, end):
    start = start.strftime("%-d-%-m-%Y")
    end = end.strftime("%-d-%-m-%Y")

    hdr = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
         'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
         'Referer': 'https://cssspritegenerator.com',
         'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
         'Accept-Encoding': 'none',
         'Accept-Language': 'en-US,en;q=0.8',
         'Connection': 'keep-alive'}

    url = BASE_URL.format(security=symbol, start_date=start, end_date=end)
    d = get(url, headers=hdr)
    soup = Soup(d.content, 'html.parser')
    payload = soup.find('div', {'id': 'csvContentDiv'}).text.replace(':', '\n')
    csv = DictReader(StringIO(payload))
    for row in csv:
        print({k:v.strip() for k, v in row.items()})


 if __name__ == '__main__':
     getquote(SECURITY_NAME, START_DATE, END_DATE)

Además, este es un fragmento relativamente modular y listo para usar.

Supreet Sethi
fuente
¡Gracias hombre! esto funcionó para mí en lugar de la respuesta anterior de @andrean
Nitish Kumar Pal
Hola, realmente ya no sé dónde golpearme la cabeza, he probado esta solución y muchas más, pero sigo recibiendo el error 403. ¿Hay algo más que pueda probar?
Francesco
El estado 403 está destinado a informar que su navegador no está autenticado para utilizar este servicio. Puede ser que en su caso, realmente requiera autenticación con autenticación básica, oauth, etc.
Supreet Sethi