【Python爬虫】利用Selenium等待Ajax加载及模拟自动翻页,爬取东方财富网公司公告

原创 2017年03月12日 16:01:49

1.背景

首先,打开东方财富网公司公告页面(“http://data.eastmoney.com/notices/”)。


单击右键,选择检查“长江电力”处的源代码,如图:


点击右键,查看源代码,查找“长江电力”,并没有在html代码里面找到“长江电力”,而只是在js代码找到。所以,可以判断,该网页采用了Ajax技术,用js动态去加载新的数据。所以,问题一:解决获得通过Ajax动态加载的数据。

然后,点击下一页,网页的url没有变化,其只是用js去加载了新的数据,然后动态改变了Table里面的数据。所以,第二个问题是:模拟翻页,也就是模拟点击网页中的“下一页”按钮。


2.解决方法

1)通过Ajax动态加载的数据

现在的网页越来越多采用了Ajax技术,这样程序便不能确定何时某个元素完全加载出来。这会让元素定位困难而且会提高产生ElementNotVisibleException的概率。

所以,Selenium提供了两种等待方式,一种是隐式等待,一种是显式等待。

隐式等待是等待特定的时间,显示等待是指定某一条件直到这个条件成立时继续执行。在这里,采用显示等待的方式,等待Table中的数据动态加载。

显示等待指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常。

def singlePageCraw(self, driver):
        try:
            element = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.ID, "dt_1"))
            )
            tr_options = element.find_elements_by_tag_name("tr")
            df = pd.DataFrame(columns = ['title', 'content'] )
            list = ['id','name','title','detail','type','time']
            for tr_option in tr_options:
                td_options = tr_option.find_elements_by_tag_name("td")
                res_data = []
                stockid = ''
                filename = ''
                count = 0
                for td_option in td_options:
                    if '更多公告 股吧 研报' in td_option.text:
                        continue
                    df.loc[count] = [list[count], str(td_option.text).strip()]
                    count = count + 1
                    if str.isdigit(str(td_option.text).strip()):
                        stockid = td_option.text
                    if ':' in td_option.text:
                        detail_url = td_option.find_element_by_tag_name("a").get_attribute('href')
                        html_cont = self.downloader.download(detail_url)
                        detail_content = self.parser.parse(detail_url, html_cont)
                        df.loc[count] = [list[count], str(detail_content).strip()]
                        filename = stockid + '_' + detail_url.split(',')[0].split('/')[-1]
                        count = count + 1
                print filename
                df.to_csv("D:/"+ filename +'.csv', index=False)
                
        except Exception,e:
            print str(e)

其中,

element = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.ID, "dt_1"))
            )

便是显示等待。程序默认会每500ms调用一次来查看元素是否已经生成,如果本来元素就是存在的,那么会立即返回。

分析页面结构可知,id为"dt_1"的table元素加载后,数据就会全部加载完毕了。接下来便是根据自己的需要,选择元素,获得元素的属性或者值。

关于元素的选取,有如下的API:

1)单个元素选取

find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector

2)多个元素选取

find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector

关于元素中值的获取:

1)获取元素的值

option.text

2)获取元素的属性值

option.get_attribute('href')

在这里,获取到了动态加载的元素之后,通过选择元素的API,获取元素的值或者属性值。

具体情况具体分析:


可以看到,在table中,每一行中,一共有六列。过滤掉第三列“更多公告 股吧 研报”,对于第四列,不仅要保存标题,还需要保存点击链接后网页中的内容。

在这里,有一个选择,一种方法是同样采用Selenium,打开网页,选择其中的元素来获取内容;另一种方法是使用urllib2下载网页,再用html.parser解析网页提取内容。考虑到打开网页需要的时间较长,而且通过分析,具体公告的内容是直接加载的,所以,采用第二种方法。

下载网页:

# coding:utf8

import urllib2
import socket
import sys

reload(sys)
sys.setdefaultencoding('utf-8')

class HtmlDownloader(object):
      
    def download(self,url):
        socket.setdefaulttimeout(200)
        if url is None:
            return None
        
        response = urllib2.urlopen(url)
        
        if response.getcode() != 200:
            return None
        return unicode(response.read(), 'GB18030', 'ignore').encode('UTF-8')
        #return response.read()


解析网页:

# coding:utf8

from baike_spider import html_downloader
from bs4 import BeautifulSoup
import sys
 
reload(sys)
sys.setdefaultencoding('utf-8')

class HtmlParser(object):
    
    def __init__(self):
        self.downloader = html_downloader.HtmlDownloader()
                   
    def _get_new_data(self, page_url, soup):
        try:   
            #<div class="detail-body" style="padding: 20px 50px;">
            return soup.find('div', class_ = 'detail-body').find('div').get_text()
        except Exception,e:
            print e
    
    def parse(self, page_url, html_cont):
        if page_url is None or html_cont is None:
            return
        
        soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')
        return self._get_new_data(page_url, soup)


2)模拟自动翻页

可以看到,点击下一页,网页的url没有发生变化。使用Selenium,模拟点击“下一页”,再使用和前面一样的方法,选择元素,就可以了。

WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.XPATH, "//a[contains(text(),'下一页')]"))
            ).click()
            time.sleep(5)


注意:这里可能会出现一个错误:

"org.openqa.selenium.WebDriverException: Element is not clickable at point (411, 675). Other element would receive the click: ..."

具体的原因和解决方法这里有详细的解释:

http://stackoverflow.com/questions/11908249/debugging-element-is-not-clickable-at-point-error

大致分为以下几种:

1.The element is not visible to click.(网页元素不可见)

Use Actions or JavascriptExecutor for making it to click.

By Actions:

WebElement element = driver.findElement(By("element_path"));

Actions actions = new Actions(driver);

actions.moveToElement(element).click().perform();

By JavascriptExecutor:

JavascriptExecutor jse = (JavascriptExecutor)driver;

jse.executeScript("scroll(250, 0)"); // if the element is on top.

jse.executeScript("scroll(0, 250)"); // if the element is on bottom.

or

JavascriptExecutor jse = (JavascriptExecutor)driver;

jse.executeScript("arguments[0].scrollIntoView()", Webelement); 

Then click on the element.

2.The page is getting refreshed before it is clicking the element.(点击元素的时候网页正在刷新)

For this, make the page to wait for few seconds.

3. The element is clickable but there is a spinner/overlay on top of it(网页上有其他元素遮挡了原本要点击的元素)

The below code will wait until the overlay disppears

By loadingImage = By.id("loading image ID");

WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);

wait.until(ExpectedConditions.invisibilityOfElementLocated(loadingImage));
结合情况1和情况2,我对上述代码进行了如上处理。


3.小结

这就是爬取东方财富网的全部要点了。这也是我学会的第二项爬虫技能。

在这里,要感谢:

Python爬虫利器五之Selenium的用法

python利用beautifulsoup+selenium自动翻页抓取网页内容

这两篇博文的作者。

完整代码课参考:

Python爬取东方财富网公司公告



版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Python-Selenium2做Web自动化测试(11)-下拉框处理、分页处理

一、下拉框处理 下拉框也是 web 页面上非常常见的功能,要想定位下拉框中的内容,首先需要定位到下拉框;引用网友的Html文件 drop_down.html ...

python网络爬虫抓取ajax动态网页数据:以抓取KFC门店地址为例

一,尝试用BeautifulSoup抓取 先打开KFC网站门店列表页面:http://www.kfc.com.cn/kfccda/storelist/index.aspx 可以看到门店列表如下图:...

python爬取ajax动态生成的数据 以抓取淘宝评论为例子

在学习python的时候,一定会遇到网站内容是通过ajax动态请求、异步刷新生成的json数据的情况,并且通过python使用之前爬取静态网页内容的方式是不可以实现的,所以这篇文章将要讲述如果在pyt...

使用python+selenium制作浏览器爬虫,彻底解决ajax异步加载问题(待更新)

打开浏览器需要下载相应的webdriver并保存到系统path下。chrome对应的webdriver下载地址:http://download.csdn.net/detail/u013760453/9...

python下利用Selenium获取动态页面数据

利用python爬取网站数据非常便捷,效率非常高,但是常用的一般都是使用BeautifSoup、requests搭配组合抓取静态页面,即网页上显示的数据都可以在html源码中找到,而不是网站通过js或...

Python爬虫使用Selenium+PhantomJS抓取Ajax和动态HTML内容

在Python网络爬虫内容提取器一文我们详细讲解了核心部件:可插拔的内容提取器类gsExtractor。本文记录了确定gsExtractor的技术路线过程中所做的编程实验。...

【Python爬虫】爬取东方财富网公司公告需要注意的几个问题

在上一篇文章中,以爬取东方财富网公司公告为例,介绍了如何爬取利用ajax加载的网页,以及如何模拟翻页。但是,在实际应用的过程中,发现了一些问题。接下来就来一一分析解决。 问题一、公告数量过多,后面的...

Python 实现股票数据的实时抓取

**最近捣鼓股票的东西,想看看股票的实时涨跌信息,又不想去看网上炒股软件现有的信息,所以寻思着自己写了一个Python的股票当前价格抓取工具,废话不多说,上代码:** 一...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

基于正则表达式(python)对东方财富网上证指数吧爬虫实例

新手爬虫
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【Python爬虫】利用Selenium等待Ajax加载及模拟自动翻页,爬取东方财富网公司公告
举报原因:
原因补充:

(最多只允许输入30个字)