python抓取动态网页(有没有什么办法可以直接获取网页的动态渲染数据呢? )
优采云 发布时间: 2021-11-14 11:02python抓取动态网页(有没有什么办法可以直接获取网页的动态渲染数据呢?
)
成功完成基金净值爬虫的爬虫后,在简单了解了爬虫的一些原理后,不禁有些疑惑——为什么不能通过Request直接获取网页的源码,但是通过查找相关的js文件来爬取数据??
有时我们使用requests抓取页面时,得到的结果可能与浏览器中看到的不同:在浏览器中可以看到正常显示的页面数据,但是使用requests获取的结果却看不到。
这是因为获取的请求都是原创的 HTML 文档,浏览器中的页面是通过 JavaScript 处理数据生成的结果。这些数据来自各种来源,可能通过 Ajax 加载或收录在 HTML 文档中。, 也可能是经过 JavaScript 和特定算法计算后生成的。按照目前Web发展的趋势,网页的原创HTML文档不收录任何数据。通过Ajax等方式统一加载后显示,从而在Web开发中实现前后端分离,减少服务器直接渲染页面。带来。通常,我们称这种网页为动态渲染页面。
之前的基金净值数据爬虫采用的是直接访问服务器获取数据接口,即找到收录数据的js文件,向服务器发送相关请求获取文件。
那么,有没有什么办法可以直接获取网页的动态渲染数据呢?答案是肯定的。
我们也可以直接用模拟浏览器操作的方式来实现动态网页的爬取,这样我们就可以在浏览器中看到它的样子,爬取到的源代码是什么,也就是实现了——可见然后爬行。
Python提供了很多模拟浏览器操作的库,如:Selenium、Splash、PyV8、Ghost等,本文继续以基金净值爬虫为例,使用Selenium对其动态页面进行爬取。
环境工具
1、Chrome 及其开发者工具
2、蟒蛇3.7
3、PyCharm
python中使用的库3.7
1、硒
2、熊猫
3、随机
4、 时间
5、操作系统
系统
Mac OS 10.13.2
Selenium 基本功能和用法
准备好工作了
ChromDriver 配置
基本使用
首先,我们先来了解一下Selenium的一些特性以及它能做什么:
Selenium 是一种自动化测试工具,可以驱动浏览器执行特定的动作,例如点击、下拉等操作。同时,它还可以获取浏览器当前呈现的页面的源代码,以便在可见时进行抓取。对于一些动态渲染的页面,这种爬取方式非常有效。它的基本功能也非常方便。下面我们来看一些简单的代码:
1 from selenium import webdriver
2 from selenium.webdriver.common.by import By
3 from selenium.webdriver.common.keys import Keys
4 from selenium.webdriver.support import expected_conditions as EC
5 from selenium.webdriver.support.wait import WebDriverWait
6
7
8 browser = webdriver.Chrome() # 声明浏览器对象
9 try:
10 browser.get(\'https://www.baidu.com\') # 传入链接URL请求网页
11 query = browser.find_element_by_id(\'kw\') # 查找节点
12 query.send_keys(\'Python\') # 输入文字
13 query.send_keys(Keys.ENTER) # 回车跳转页面
14 wait = WebDriverWait(browser, 10) # 设置最长加载等待时间
15 print(browser.current_url) # 获取当前URL
16 print(browser.get_cookies()) # 获取当前Cookies
17 print(browser.page_source) # 获取源代码
18 finally:
19 browser.close() # 关闭浏览器
运行代码后,Chrome 浏览器会自动弹出。浏览器会跳转到百度,然后在搜索框中输入Python→回车→跳转到搜索结果页面,得到结果后关闭浏览器。这相当于模拟了我们在百度上搜索Python的全套动作,给你带来惊喜!!
在这个过程中,当加载网页结果时,控制台会分别输出当前的URL、当前的Cookies和网页源代码:
可以看出,我们得到的内容是浏览器中的真实内容。可见,使用Selenium驱动浏览器加载网页,可以直接得到JavaScript渲染的结果。接下来我们也会主要用Selenium来爬取基金的净值~
注:Selenium更详细的用法和功能可以在官网查看()
基金净值数据爬虫
通过前面的爬虫,我们会发*敏*感*词*净值数据爬虫。
页面分析
这个爬虫的目标是单个基金的净值数据。抓取到的网址为:(以单个基金519961为例)。URL 的结构是显而易见的。当我们在浏览器中输入访问链接时,会显示最新的。基金权益数据第一页结果:
在数据下方,有一个页面导航,其中包括前五页之间的链接以及上一页和下一页之间的链接。还有一个链接可以输入任何要跳转到的页码:
如果我们想要获取第二页及以后的数据,我们需要跳转到相应的页码。因此,如果我们需要获取所有的历史净值数据,只需要遍历所有的页码即可。可以直接在页面跳转文本框中输入要跳转到的页码,然后点击“确定”按钮跳转到对应页码的页面。
这里没有直接点击“下一页”的原因是:一旦在爬取过程中出现异常退出,比如在第50页退出时,此时点击“下一页”就无法快速切换到当前页面相应的后续页面。另外,在爬取过程中需要记录当前爬虫的进度,以便及时进行异常检测,检测问题在第一页。整个过程比较复杂,采用直接跳转的方式抓取网页更合理。
当我们成功加载了某个页面的净值数据后,我们就可以使用Selenium来获取该页面的源代码了。定位到一个特定的节点后,我们就可以得到目标的HTML内容,然后通过相应的分析就可以得到我们的HTML内容。目标数据。下面,我们用代码来实现整个爬取过程。
获取基金权益清单
首先,我们需要构造目标 URL。这里的URL组成规则很明显,就是基金code.html。我们可以使用规则来构造我们要爬取的基金对象。这里以基金519961为例进行爬取。
1 browser = webdriver.Chrome()
2 wait = WebDriverWait(browser, 10)
3 fundcode=\'519961\'
4
5 def index_page(page):
6 \'\'\'
7 抓取基金索引页
8 :param page: 页码
9 :param fundcode: 基金代码
10 \'\'\'
11 print(\'正在爬取基金%s第%d页\' % (fundcode, page))
12 try:
13 url = \'http://fundf10.eastmoney.com/jjjz_%s.html\' % fundcode
14 browser.get(url)
15 if page>1:
16 input_page = wait.until(
17 EC.presence_of_element_located((By.CSS_SELECTOR, \'#pagebar input.pnum\')))
18 submit = wait.until(
19 EC.element_to_be_clickable((By.CSS_SELECTOR, \'#pagebar input.pgo\')))
20 input_page.clear()
21 input_page.send_keys(str(page))
22 submit.click()
23 wait.until(
24 EC.text_to_be_present_in_element((By.CSS_SELECTOR, \'#pagebar label.cur\'),
25 str(page)))
26 wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, \'#jztable\')))
27 get_jjjz()
28 except TimeoutException:
29 index_page(page)
这里首相构造了一个WebDriver对象,即声明浏览器对象,使用的浏览器是Chrome,然后指定一个基金代码(519961),然后定义index_page()方法来抓取列表基金净值数据。
在这种方法中,我们首先访问链接搜索资金,然后判断当前页码。如果大于1,我们会跳转到页面,否则我们将等待页面加载。
在等待加载时,我们使用WebDriverWait对象,它可以指定等待条件,同时制定一个最大等待时间,这里指定为最长10秒。如果在这段时间内满足等待条件,即页面元素加载成功,则立即返回相应结果并继续向下执行,否则,当最大等待时间未加载时,直接抛出一个超市例外。
比如我们最后需要等待历史权益信息加载完毕并指定presence_of_element_located条件,然后传入CSS选择器对应的条件#jztable,这个选择器对应的页面内容就是每个基金权益数据页面。在网页上查看:
注意:这里有一个小技巧。如果同学们对CSS选择器的语法不熟悉,可以右键选中节点→复制→复制选择器,直接获取对应的选择器:
CSS 选择器的语法请参考 CSS 选择器参考手册 ()。
加载成功后,机会订单的后续get_jjjz()方法提取历史净值信息。
关于翻页操作,这里先获取页码输入框,赋值给input_page,然后获取“OK”按钮,赋值给submit:
首先,我们需要在输入框中调用clear()方法(不管输入框是否有页码数据)。然后,调用send_keys()方法将页码填入输入框,然后点击“确定”按钮,听起来和我们正常的操作方法一样。
那么,如何知道是否跳转到了对应的页码呢?我们可以注意到,当跳转到当前页面时,页码会高亮显示:
我们只需要判断当前高亮的页码就是当前页码,这里左移使用了另一个等待条件text_to_be_present_in_element,它会等待指定的文本出现在某个节点然后返回成功,这里我们就高亮了亮页码和当前要跳转到的页码对应的CSS选择器通过参数传递给这个等待条件,这样它就会检查当前高亮页码节点是否是我们传递过来的页码数,如果是即,证明页面成功跳转到此页面,页面跳转成功。
这样,刚刚实现的index_page()方法就可以传入对应的页码,加载完页码对应的商品列表后,调用get_jjjz()方法进行页面分析。
解析历史净值数据列表
接下来,我们可以实现 get_jjjz() 方法来解析历史净值数据列表。在这里,我们通过搜索所有历史权益数据节点来获取对应的HTML内容
并进行相应的分析,实现如下:
1 def get_jjjz():
2 \'\'\'
3 提取基金净值数据
4 \'\'\'
5 lsjz = pd.DataFrame()
6 html_list = browser.find_elements_by_css_selector(\'#jztable tbody tr\')
7 for html in html_list:
8 data = html.text.split(\' \')
9 datas = {
10 \'净值日期\': data[0],
11 \'单位净值\': data[1],
12 \'累计净值\': data[2],
13 \'日增长率\': data[3],
14 \'申购状态\': data[4],
15 \'赎回状态\': data[5],
16 }
17 lsjz = lsjz.append(datas, ignore_index=True)
18 save_to_csv(lsjz)
首先调用 find_elements_by_css_selector 获取所有存储历史权益数据的节点。此时使用的CSS选择器是#jztable tbody tr,会匹配所有基金权益节点,输出是一个打包成列表的HTML。使用for循环遍历列表,使用text方法提取每个html中的文本内容,得到的输出是空格分隔的字符串数据。为了方便后续处理,我们可以使用split方法将数据拆分成一个新的以列表形式存储,再转换为dict形式。
最后,为了方便处理,我们将遍历的数据存储为DataFrame,然后使用save_to_csv()方法存储为csv文件。
另存为本地 csv 文件
接下来,我们将获取到的基金历史股权数据保存为本地csv文件。实现代码如下:
1 def save_to_csv(lsjz):
2 \'\'\'
3 保存为csv文件
4 : param result: 历史净值
5 \'\'\'
6 file_path = \'lsjz_%s.csv\' % fundcode
7 try:
8 if not os.path.isfile(file_path): # 判断当前目录下是否已存在该csv文件,若不存在,则直接存储
9 lsjz.to_csv(file_path, index=False)
10 else: # 若已存在,则追加存储,并设置header参数为False,防止列名重复存储
11 lsjz.to_csv(file_path, mode=\'a\', index=False, header=False)
12 print(\'存储成功\')
13 except Exception as e:
14 print(\'存储失败\')
这里,结果变量是 get_jjjz() 方法中传递的历史权益数据。
遍历每一页
我们之前定义的get_index()方法需要接受参数page,代表页码。这里,因为不同基金的数据页不一样,我们需要得到最大页数才能遍历所有页。当然,我们也可以用一些巧妙的方法来解决这个问题。页码遍历代码如下:
1 def main():
2 \'\'\'
3 遍历每一页
4 \'\'\'
5 flag = True
6 page = 1
7 while flag:
8 try:
9 index_page(page)
10 time.sleep(random.randint(1, 5))
11 page += 1
12 except:
13 flag = False
14 print(\'似乎是最后一页了呢\')
它的实现方法结合了try...except和while方法来逐页遍历下一页的内容。当页数超过时,即不存在,index_page()运行时会报错。这时候可以把flag改成False,那么下一次while循环就不会继续了,这样我们就可以遍历所有的页码了。
至此,我们的基金净值数据爬虫已经基本完成,终于可以直接调用main()方法运行了。
总结
在本文中,我们使用 Selenium 来演示基金股权页面的抓取。有兴趣的同学可以尝试使用其他条件来爬取基金数据,比如设置数据的起止日期:
使用日期来抓取内容可以促进未来的数据更新。另外,如果觉得浏览器弹窗比较烦,可以试试Chrome Headless模式或者使用PhantomJS爬取。
至此,基金净值爬虫的分析正式结束。