¿Cómo puedo raspar páginas con URL generadas dinámicamente usando Python?

Estoy intentando eliminar http://www.dailyfinance.com/quote/NYSE/international-business-machines/IBM/financial-ratios , pero la técnica tradicional de creación de cadenas de url no funciona porque el “nombre completo de la compañía “es insertada en la ruta”. Y el nombre exacto de la compañía no se conoce de antemano. Sólo se conoce el símbolo de la empresa, “IBM”.

Esencialmente, la forma en que lo raspo es haciendo un bucle a través de un conjunto de símbolos de la compañía y comstackr la cadena url antes de enviarla a urllib2.urlopen (url). Pero en este caso, eso no se puede hacer.

Por ejemplo, la cadena CSCO es

http://www.dailyfinance.com/quote/NASDAQ/cisco-systems-inc/CSCO/financial-ratios 

y otra cadena url de ejemplo es AAPL:

 http://www.dailyfinance.com/quote/NASDAQ/apple/AAPL/financial-ratios 

Así que para obtener la url, tuve que buscar el símbolo en el cuadro de entrada en la página principal:

 http://www.dailyfinance.com/ 

He notado que cuando escribo “CSCO” e inspecciono la entrada de búsqueda en ( http://www.dailyfinance.com/quote/NASDAQ/apple/AAPL/financial-ratios en la pestaña de la red de desarrolladores web de Firefox, noté que la obtener solicitud es enviar a

 http://j.foolcdn.com/tmf/predictivesearch?callback=_predictiveSearch_csco&term=csco&domain=dailyfinance.com 

y que el referente realmente da el camino que quiero capturar

 Host: j.foolcdn.com User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://www.dailyfinance.com/quote/NASDAQ/cisco-systems-inc/CSCO/financial-ratios?source=itxwebtxt0000007 Connection: keep-alive 

Lo siento por la larga explicación. Entonces la pregunta es ¿cómo extraigo la url en el Referer? Si eso no es posible, ¿cómo debo abordar este problema? ¿Hay otra manera?

Realmente aprecio tu ayuda.

Me gusta esta pregunta. Y debido a eso, voy a dar una respuesta muy completa. Para esto, usaré mi biblioteca de solicitudes favoritas junto con BeautifulSoup4. Pasar a Mechanize si realmente quieres usar eso depende de ti. Sin embargo, las solicitudes le ahorrarán toneladas de dolores de cabeza.


En primer lugar, es probable que estés buscando una solicitud POST. Sin embargo, las solicitudes POST a menudo no son necesarias si una función de búsqueda lo lleva de inmediato a la página que está buscando. Así que vamos a inspeccionarlo, ¿de acuerdo?

Cuando llego a la URL base, http://www.dailyfinance.com/ , puedo hacer una comprobación simple a través de Firebug o la herramienta de inspección de Chrome que cuando coloco CSCO o AAPL en la barra de búsqueda y habilito el “salto”, hay un 301 Moved Permanently Código de estado 301 Moved Permanently . ¿Qué significa esto?

introduzca la descripción de la imagen aquí

En términos simples, fui transferido a algún lugar. La URL para esta solicitud GET es la siguiente:

 http://www.dailyfinance.com/quote/jump?exchange-input=&ticker-input=CSCO 

Ahora, probamos si funciona con AAPL utilizando una manipulación de URL simple.

 import requests as rq apl_tick = "AAPL" url = "http://www.dailyfinance.com/quote/jump?exchange-input=&ticker-input=" r = rq.get(url + apl_tick) print r.url 

Lo anterior da el siguiente resultado:

 http://www.dailyfinance.com/quote/nasdaq/apple/aapl [Finished in 2.3s] 

¿Ves cómo cambió la URL de la respuesta? Vamos a llevar la manipulación de URL un paso más allá al buscar la página /financial-ratios agregando el código siguiente al código anterior:

 new_url = r.url + "/financial-ratios" p = rq.get(new_url) print p.url 

Cuando se ejecuta, esto da es el siguiente resultado:

 http://www.dailyfinance.com/quote/nasdaq/apple/aapl http://www.dailyfinance.com/quote/nasdaq/apple/aapl/financial-ratios [Finished in 6.0s] 

Ahora estamos en el camino correcto. Ahora trataré de analizar los datos usando BeautifulSoup. Mi código completo es el siguiente:

 from bs4 import BeautifulSoup as bsoup import requests as rq apl_tick = "AAPL" url = "http://www.dailyfinance.com/quote/jump?exchange-input=&ticker-input=" r = rq.get(url + apl_tick) new_url = r.url + "/financial-ratios" p = rq.get(new_url) soup = bsoup(p.content) div = soup.find("div", id="clear").table rows = table.find_all("tr") for row in rows: print row 

Luego trato de ejecutar este código, solo para encontrar un error con el siguiente rastreo:

  File "C:\Users\nanashi\Desktop\test.py", line 13, in  div = soup.find("div", id="clear").table AttributeError: 'NoneType' object has no attribute 'table' 

Es de destacar la línea 'NoneType' object... Esto significa que nuestro div objective no existe! Egads, pero ¿por qué estoy viendo lo siguiente?

introduzca la descripción de la imagen aquí

Solo puede haber una explicación: ¡la tabla se carga dinámicamente! Las ratas A ver si podemos encontrar otra fuente para la tabla. Estudio la página y veo que hay barras de desplazamiento en la parte inferior. Esto podría significar que la tabla se cargó dentro de un marco o se cargó directamente de otra fuente y se colocó en un div en la página.

Actualizo la página y vuelvo a ver las solicitudes GET. Bingo, encontré algo que parece un poco prometedor:

introduzca la descripción de la imagen aquí

Una URL de origen de un tercero, y es fácil de manipular usando el símbolo del marcador. Intentemos cargarlo en una nueva pestaña. Esto es lo que obtenemos:

introduzca la descripción de la imagen aquí

¡GUAUU! Ahora tenemos la fuente muy exacta de nuestros datos. Sin embargo, el último obstáculo es si funcionará cuando intentemos extraer los datos de CSCO usando esta cadena (recuerde que fuimos CSCO -> AAPL y ahora volvemos a CSCO nuevamente, así que no está confundido). Limpiemos la cadena y abandonemos el papel de www.dailyfinance.com aquí completamente. Nuestra nueva url es la siguiente:

 http://www.motleyfool.idmanagedsolutions.com/stocks/financial_ratios.idms?SYMBOL_US=AAPL 

¡Intentemos usar eso en nuestro raspador final!

 from bs4 import BeautifulSoup as bsoup import requests as rq csco_tick = "CSCO" url = "http://www.motleyfool.idmanagedsolutions.com/stocks/financial_ratios.idms?SYMBOL_US=" new_url = url + csco_tick r = rq.get(new_url) soup = bsoup(r.content) table = soup.find("div", id="clear").table rows = table.find_all("tr") for row in rows: print row.get_text() 

Y nuestros resultados sin procesar para los datos de ratios financieros de CSCO son los siguientes:

 Company Industry Valuation Ratios P/E Ratio (TTM) 15.40 14.80 P/E High - Last 5 Yrs 24.00 28.90 P/E Low - Last 5 Yrs 8.40 12.10 Beta 1.37 1.50 Price to Sales (TTM) 2.51 2.59 Price to Book (MRQ) 2.14 2.17 Price to Tangible Book (MRQ) 4.25 3.83 Price to Cash Flow (TTM) 11.40 11.60 Price to Free Cash Flow (TTM) 28.20 60.20 Dividends Dividend Yield (%) 3.30 2.50 Dividend Yield - 5 Yr Avg (%) NA 1.20 Dividend 5 Yr Growth Rate (%) NA 144.07 Payout Ratio (TTM) 45.00 32.00 Sales (MRQ) vs Qtr 1 Yr Ago (%) -7.80 -3.70 Sales (TTM) vs TTM 1 Yr Ago (%) 5.50 5.60 Growth Rates (%) Sales - 5 Yr Growth Rate (%) 5.51 5.12 EPS (MRQ) vs Qtr 1 Yr Ago (%) -54.50 -51.90 EPS (TTM) vs TTM 1 Yr Ago (%) -54.50 -51.90 EPS - 5 Yr Growth Rate (%) 8.91 9.04 Capital Spending - 5 Yr Growth Rate (%) 20.30 20.94 Financial Strength Quick Ratio (MRQ) 2.40 2.70 Current Ratio (MRQ) 2.60 2.90 LT Debt to Equity (MRQ) 0.22 0.20 Total Debt to Equity (MRQ) 0.31 0.25 Interest Coverage (TTM) 18.90 19.10 Profitability Ratios (%) Gross Margin (TTM) 63.20 62.50 Gross Margin - 5 Yr Avg 66.30 64.00 EBITD Margin (TTM) 26.20 25.00 EBITD - 5 Yr Avg 28.82 0.00 Pre-Tax Margin (TTM) 21.10 20.00 Pre-Tax Margin - 5 Yr Avg 21.60 18.80 Management Effectiveness (%) Net Profit Margin (TTM) 17.10 17.65 Net Profit Margin - 5 Yr Avg 17.90 15.40 Return on Assets (TTM) 8.30 8.90 Return on Assets - 5 Yr Avg 8.90 8.00 Return on Investment (TTM) 11.90 12.30 Return on Investment - 5 Yr Avg 12.50 10.90 Efficiency Revenue/Employee (TTM) 637,890.00 556,027.00 Net Income/Employee (TTM) 108,902.00 98,118.00 Receivable Turnover (TTM) 5.70 5.80 Inventory Turnover (TTM) 11.30 9.70 Asset Turnover (TTM) 0.50 0.50 [Finished in 2.0s] 

La limpieza de los datos depende de usted.


Una buena lección que aprender de este rasguño es que no todos los datos están contenidos en una sola página. Es muy agradable verlo desde otro sitio estático. Si fue producido a través de llamadas de JavaScript o AJAX o similares, probablemente tendríamos algunas dificultades con nuestro enfoque.

Esperemos que hayas aprendido algo de esto. Háganos saber si esto ayuda y buena suerte.

No responde a su pregunta específica, pero resuelve su problema.

http://www.dailyfinance.com/quotes/{Company Symbol}/{Stock Exchange}

Ejemplos:

http://www.dailyfinance.com/quotes/AAPL/NAS

http://www.dailyfinance.com/quotes/IBM/NYSE

http://www.dailyfinance.com/quotes/CSCO/NAS

Para llegar a la página de índices financieros, entonces podrías emplear algo como esto:

 import urllib2 def financial_ratio_url(symbol, stock_exchange): starturl = 'http://www.dailyfinance.com/quotes/' starturl += '/'.join([symbol, stock_exchange]) req = urllib2.Request(starturl) res = urllib2.urlopen(starturl) return '/'.join([res.geturl(),'financial-ratios']) 

Ejemplo:

 financial_ratio_url('AAPL', 'NAS') 'http://www.dailyfinance.com/quote/nasdaq/apple/aapl/financial-ratios'