urllib2.HTTPError: Erro HTTP 403: Proibido

102

Estou tentando automatizar o download de dados de estoque históricos usando python. A URL que estou tentando abrir responde com um arquivo CSV, mas não consigo abrir usando urllib2. Eu tentei mudar o agente do usuário conforme especificado em algumas perguntas anteriores, até tentei aceitar cookies de resposta, sem sorte. Você pode por favor ajudar.

Observação: o mesmo método funciona para o 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)

Erro

Arquivo "C: \ Python27 \ lib \ urllib2.py", linha 527, em http_error_default, aumentar HTTPError (req.get_full_url (), código, msg, hdrs, fp) urllib2.HTTPError: Erro HTTP 403: Proibido

obrigado pela ajuda

Kumar
fonte
Você usa o Windows como plataforma?
Denis

Respostas:

170

Ao adicionar mais alguns cabeçalhos, consegui obter os dados:

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

Na verdade, ele funciona apenas com este cabeçalho adicional:

'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
andrean
fonte
Qual desses cabeçalhos você acha que estava faltando na solicitação original?
1
WireShark mostrou que apenas o User-Agent foi enviado, junto com Connection: close, Host: www.nseindia.com, Accept-Encoding: identity
andrean
1
De nada, bem, o que eu realmente fiz foi verificar o url do seu script em um navegador e, como funcionou lá, apenas copiei todos os cabeçalhos de solicitação que o navegador enviou e os adicionei aqui, e essa foi a solução.
andrean
1
@Mee, você deu uma olhada na resposta abaixo? foi endereçado especificamente para python 3, verifique se funciona para você ...
andrean
1
tente adicionar os outros cabeçalhos (da minha resposta) também à solicitação. ainda existem muitos outros motivos pelos quais um servidor pode retornar um 403, verifique também as outras respostas sobre o tópico. quanto ao alvo, o google em especial é difícil, meio difícil de raspar, eles implementaram muitos métodos para evitar a raspagem.
andrean
50

Isso funcionará em 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
fonte
2
É verdade que alguns sites (incluindo a Wikipedia) bloqueiam strings de agentes de usuários comuns não navegadores, como o "Python-urllib / xy" enviado pelas bibliotecas do Python. Mesmo um simples "Mozilla" ou "Opera" geralmente é suficiente para contornar isso. Isso não se aplica à pergunta original, é claro, mas ainda é útil saber.
efotinis
7

O site da NSE mudou e os scripts mais antigos são semi-ideais para o site atual. Este snippet pode reunir detalhes diários de segurança. Os detalhes incluem símbolo, tipo de título, fechamento anterior, preço de abertura, preço alto, preço baixo, preço médio, quantidade negociada, volume de negócios, número de negociações, quantidades entregues e proporção de entregues vs negociados em porcentagem. Estes convenientemente apresentados como uma lista de forma de dicionário.

Versão Python 3.X com solicitações e 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)

Além disso, isso é relativamente modular e está pronto para usar o snippet.

Supreet Sethi
fonte
Obrigado, cara! isso funcionou para mim ao invés da resposta acima de @andrean
Nitish Kumar Pal
Olá, realmente não sei mais onde bater a cabeça. Tentei essa solução e muitas outras, mas continuo recebendo o erro 403. Posso tentar mais alguma coisa?
Francesco
O status 403 serve para informar que seu navegador não está autenticado para usar este serviço. Pode ser que, no seu caso, ele realmente exija autenticação com autenticação básica, oauth etc.
Supreet Sethi