从使用 python 对象呈现 url 的 javascript 解析 html
Parsing html from a javascript rendered url with python object
我想从以下 url 及其所有后续页面中提取市场信息:
https://uk.reuters.com/investing/markets/index/.FTSE?sortBy=&sortDir=&pn=1
我已经使用以下 url:
中的一些代码成功地从第一页解析了我想要的数据
https://impythonist.wordpress.com/2015/01/06/ultimate-guide-for-scraping-javascript-rendered-web-pages
我还能够解析出下一页的 url 以输入循环以便从下一页获取数据。问题是由于我不完全理解的原因,它在加载下一页之前崩溃了。
我有一种预感,我从 'impythonist' 那里借来的 class 可能是导致问题的原因。我不知道足够的面向对象编程来解决这个问题。这是我的代码,其中大部分是从上面的 url 借来的:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtWebKit import *
from lxml import html
import re
from bs4 import BeautifulSoup
class Render(QWebPage):
def __init__(self, url):
self.app = QApplication(sys.argv)
QWebPage.__init__(self)
self.loadFinished.connect(self._loadFinished)
self.mainFrame().load(QUrl(url))
self.app.exec_()
def _loadFinished(self, result):
self.frame = self.mainFrame()
self.app.quit()
base_url='https://uk.reuters.com'
complete_next_page='https://uk.reuters.com/investing/markets/index/.FTSE?sortBy=&sortDir=&pn=1'
#LOOP TO RENDER PAGES AND GRAB DATA
while complete_next_page != '':
print ('NEXT PAGE: ',complete_next_page, '\n')
r = Render(complete_next_page) # USE THE CLASS TO RENDER JAVASCRIPT FROM PAGE
result = r.frame.toHtml() # ERROR IS THROWN HERE ON 2nd PAGE
# PARSE THE HTML
soup = BeautifulSoup(result, 'lxml')
row_data=soup.find('div', attrs={'class':'column1 gridPanel grid8'})
print (len(row_data))
# PARSE ALL ROW DATA
stripe_rows=row_data.findAll('tr', attrs={'class':'stripe'})
non_stripe_rows=row_data.findAll('tr', attrs={'class':''})
print (len(stripe_rows))
print (len(non_stripe_rows))
# PARSE SPECIFIC ROW DATA FROM INDEX COMPONENTS
#non_stripe_rows: from 4 to 18 (inclusive) contain data
#stripe_rows: from 2 to 16 (inclusive) contain data
i=2
while i < len(stripe_rows):
print('CURRENT LINE IS: ',str(i))
print(stripe_rows[i])
print('###############################################')
print(non_stripe_rows[i+2])
print('\n')
i+=1
#GETS LINK TO NEXT PAGE
next_page=str(soup.find('div', attrs={'class':'pageNavigation'}).find('li', attrs={'class':'next'}).find('a')['href']) #GETS LINK TO NEXT PAGE WORKS
complete_next_page=base_url+next_page
我已经注释了我编写和理解的代码位,但我真的不知道 'Render' class 中发生了什么足以诊断错误?除非是别的什么?
这里是错误:
result = r.frame.toHtml()
AttributeError: 'Render' object has no attribute 'frame'
一旦我解析了它,我就不需要将信息保留在 class 中,所以我在想也许它可以以某种方式被清除或重置,然后更新以保存新的 url 来自 2:n 页的信息,但我不知道该怎么做?
或者,如果有人知道从该页面和后续页面获取此特定数据的另一种方法,那同样有用吗?
非常感谢。
用 selenium 和 phantomjs 代替 PyQt 怎么样?
您可以通过执行 "pip install selenium" 轻松获取硒。
如果你使用 Mac 你可以通过执行 "brew install phantomjs" 来获得 phantomjs。
如果您的 PC Windows 使用 choco 而不是 brew,或者 Ubuntu 使用 apt-get。
from selenium import webdriver
from bs4 import BeautifulSoup
base_url = "https://uk.reuters.com"
first_page = "/business/markets/index/.FTSE?sortBy=&sortDir=&pn=1"
browser = webdriver.PhantomJS()
# PARSE THE HTML
browser.get(base_url + first_page)
soup = BeautifulSoup(browser.page_source, "lxml")
row_data = soup.find('div', attrs={'class':'column1 gridPanel grid8'})
# PARSE ALL ROW DATA
stripe_rows = row_data.findAll('tr', attrs={'class':'stripe'})
non_stripe_rows = row_data.findAll('tr', attrs={'class':''})
print(len(stripe_rows), len(non_stripe_rows))
# GO TO THE NEXT PAGE
next_button = soup.find("li", attrs={"class":"next"})
while next_button:
next_page = next_button.find("a")["href"]
browser.get(base_url + next_page)
soup = BeautifulSoup(browser.page_source, "lxml")
row_data = soup.find('div', attrs={'class':'column1 gridPanel grid8'})
stripe_rows = row_data.findAll('tr', attrs={'class':'stripe'})
non_stripe_rows = row_data.findAll('tr', attrs={'class':''})
print(len(stripe_rows), len(non_stripe_rows))
next_button = soup.find("li", attrs={"class":"next"})
# DONT FORGET THIS!!
browser.quit()
我知道上面的代码效率不高(我觉得太慢了),但我认为它会给你带来你想要的结果。另外,如果你要抓取的网页没有使用Javascript,连PhantomJS和selenium都不需要。您可以使用请求模块。但是,因为我想向您展示与 PyQt 的对比,所以我在这个答案中使用了 PhantomJS 和 Selenium。
我想从以下 url 及其所有后续页面中提取市场信息:
https://uk.reuters.com/investing/markets/index/.FTSE?sortBy=&sortDir=&pn=1
我已经使用以下 url:
中的一些代码成功地从第一页解析了我想要的数据https://impythonist.wordpress.com/2015/01/06/ultimate-guide-for-scraping-javascript-rendered-web-pages
我还能够解析出下一页的 url 以输入循环以便从下一页获取数据。问题是由于我不完全理解的原因,它在加载下一页之前崩溃了。
我有一种预感,我从 'impythonist' 那里借来的 class 可能是导致问题的原因。我不知道足够的面向对象编程来解决这个问题。这是我的代码,其中大部分是从上面的 url 借来的:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtWebKit import *
from lxml import html
import re
from bs4 import BeautifulSoup
class Render(QWebPage):
def __init__(self, url):
self.app = QApplication(sys.argv)
QWebPage.__init__(self)
self.loadFinished.connect(self._loadFinished)
self.mainFrame().load(QUrl(url))
self.app.exec_()
def _loadFinished(self, result):
self.frame = self.mainFrame()
self.app.quit()
base_url='https://uk.reuters.com'
complete_next_page='https://uk.reuters.com/investing/markets/index/.FTSE?sortBy=&sortDir=&pn=1'
#LOOP TO RENDER PAGES AND GRAB DATA
while complete_next_page != '':
print ('NEXT PAGE: ',complete_next_page, '\n')
r = Render(complete_next_page) # USE THE CLASS TO RENDER JAVASCRIPT FROM PAGE
result = r.frame.toHtml() # ERROR IS THROWN HERE ON 2nd PAGE
# PARSE THE HTML
soup = BeautifulSoup(result, 'lxml')
row_data=soup.find('div', attrs={'class':'column1 gridPanel grid8'})
print (len(row_data))
# PARSE ALL ROW DATA
stripe_rows=row_data.findAll('tr', attrs={'class':'stripe'})
non_stripe_rows=row_data.findAll('tr', attrs={'class':''})
print (len(stripe_rows))
print (len(non_stripe_rows))
# PARSE SPECIFIC ROW DATA FROM INDEX COMPONENTS
#non_stripe_rows: from 4 to 18 (inclusive) contain data
#stripe_rows: from 2 to 16 (inclusive) contain data
i=2
while i < len(stripe_rows):
print('CURRENT LINE IS: ',str(i))
print(stripe_rows[i])
print('###############################################')
print(non_stripe_rows[i+2])
print('\n')
i+=1
#GETS LINK TO NEXT PAGE
next_page=str(soup.find('div', attrs={'class':'pageNavigation'}).find('li', attrs={'class':'next'}).find('a')['href']) #GETS LINK TO NEXT PAGE WORKS
complete_next_page=base_url+next_page
我已经注释了我编写和理解的代码位,但我真的不知道 'Render' class 中发生了什么足以诊断错误?除非是别的什么?
这里是错误:
result = r.frame.toHtml()
AttributeError: 'Render' object has no attribute 'frame'
一旦我解析了它,我就不需要将信息保留在 class 中,所以我在想也许它可以以某种方式被清除或重置,然后更新以保存新的 url 来自 2:n 页的信息,但我不知道该怎么做?
或者,如果有人知道从该页面和后续页面获取此特定数据的另一种方法,那同样有用吗?
非常感谢。
用 selenium 和 phantomjs 代替 PyQt 怎么样?
您可以通过执行 "pip install selenium" 轻松获取硒。
如果你使用 Mac 你可以通过执行 "brew install phantomjs" 来获得 phantomjs。
如果您的 PC Windows 使用 choco 而不是 brew,或者 Ubuntu 使用 apt-get。
from selenium import webdriver
from bs4 import BeautifulSoup
base_url = "https://uk.reuters.com"
first_page = "/business/markets/index/.FTSE?sortBy=&sortDir=&pn=1"
browser = webdriver.PhantomJS()
# PARSE THE HTML
browser.get(base_url + first_page)
soup = BeautifulSoup(browser.page_source, "lxml")
row_data = soup.find('div', attrs={'class':'column1 gridPanel grid8'})
# PARSE ALL ROW DATA
stripe_rows = row_data.findAll('tr', attrs={'class':'stripe'})
non_stripe_rows = row_data.findAll('tr', attrs={'class':''})
print(len(stripe_rows), len(non_stripe_rows))
# GO TO THE NEXT PAGE
next_button = soup.find("li", attrs={"class":"next"})
while next_button:
next_page = next_button.find("a")["href"]
browser.get(base_url + next_page)
soup = BeautifulSoup(browser.page_source, "lxml")
row_data = soup.find('div', attrs={'class':'column1 gridPanel grid8'})
stripe_rows = row_data.findAll('tr', attrs={'class':'stripe'})
non_stripe_rows = row_data.findAll('tr', attrs={'class':''})
print(len(stripe_rows), len(non_stripe_rows))
next_button = soup.find("li", attrs={"class":"next"})
# DONT FORGET THIS!!
browser.quit()
我知道上面的代码效率不高(我觉得太慢了),但我认为它会给你带来你想要的结果。另外,如果你要抓取的网页没有使用Javascript,连PhantomJS和selenium都不需要。您可以使用请求模块。但是,因为我想向您展示与 PyQt 的对比,所以我在这个答案中使用了 PhantomJS 和 Selenium。