selenium 是一个web的自动化测试工具,支持多平台:windows、linux、MAC ,支持多浏览器:ie、ff、safari、opera、chrome,支持多语言:例如C、JAVA、Python等,支持分布式测试用例的执行,可以把测试用例分布到不同的测试机器的执行,相当于分发机的功能。
虽然Selenium本来是应用于自动化测试领域,但是因为Selenium可以实现Web交互操作,所以可以利用Selenium模拟Web抓取一些常规方式不能抓取的数据,例如一些页面生成后才会动态加载的数据,或者需要登录后才能访问的数据。
下面以获取某股票网站的板块行情数据为例子,介绍Python结合Selenium模拟网页抓取数据的方法:
1、首先配置Python + Selenium环境
(1)访问http://www.python.org/download/去下载合适的python版本,推荐3.4或2.7版本。
(2)安装下载包,一路next。
(3)把python的安装目录添加到path系统变量中即可。
(4)测试python安装是否成功,cmd打开命令行输入 python命令,如下图即成功了
(5)安装PIP、SetupTools
安装地址: http://pypi.python.org/pypi/setuptools、https://pypi.python.org/pypi/pip
(6)安装Selenium
执行命令 pip install -U selenium
2、分析其页面结构
页面包括了股票列表和页面列表
股票列表通过id为list-body的ul展示:
页面列表是一个id为page-navi的div,但是这个div是根据股票数据动态生成的。
3、程序逻辑
通过页面分析,初步的程序逻辑:
a. 首先通过Selenium的chrome或者firefox驱动加载页面
b. 通过页面元素抓取数据
c. 通过Click()方法实现页面跳转
d. 重复a-c直到页面全部跳转完成
4、代码实现:
(1)获取板块数据
'''获取行业板块数据''' def getIndustrySection(): __logger.debug("开始:收集行业板块数据") try: dbOperator = DBOperator() dbOperator.connDB() table = __stockTables['section'] date = getNowdate() for code in range(1, 100): code = '012%03d' % code if isStockSectionExitsInDate(table,code, date, dbOperator): dataUrl = "http://…………fi_quote_navi_bar&id=bd_ind#mod=list&id=bd%s"% code getStockSectionDetail(code,dataUrl,dbOperator) except Exception as err: __logger.error(">>>>>> Exception: "+str(code) + " " + str(err)) finally: dbOperator.closeDB() __logger.debug("结束:收集行业板块数据")
(2)抓取代码
'''读取信息''' def getDataFromUrl(dataUrl): try: browser = webdriver.Firefox() browser.get(dataUrl) time.sleep(2) codes = [] pages = [1,] pageTotalNum = 1 listFoot =browser.find_element_by_class_name("list-foot") pageTags =listFoot.find_elements_by_tag_name("a") pageTotalNum = len(pageTags) - 2 for i in range(1, pageTotalNum ): element =browser.find_element_by_xpath("//ul[contains(@id,'list-body')]") codeElements =element.find_elements_by_tag_name("li") for codeElement in codeElements: codes.append(codeElement.get_attribute("id")[-6:]) listFoot =browser.find_element_by_class_name("list-foot") pageTags =listFoot.find_elements_by_tag_name("a") nextPage = i + 1 if i < pageTotalNum and not nextPage in pages: pageTags[nextPage].click() pages.append(nextPage) time.sleep(2) print codes except NoSuchElementException as err: __logger.error(">>>>>> Exception: " + str(err)) return None except TimeoutException as err: __logger.error(">>>>>> Exception: " + str(err)) return None except Exception as err: __logger.error(">>>>>> Exception: " + str(err)) return None finally: browser.close() return codes
(3)调用抓取数据并存储数据库
'''获取板块交易信息明细''' def getStockSectionDetail(sectionCode, dataUrl, dbOperator): stockCodes = getDataFromUrl(dataUrl) if stockCodes == None and len(stockCodes)== 0: return False stockQuotation = {} date = getNowDate() for stockCode in stockCodes: #存储到数据库 ……
(4)多线程实现
为提高运行效率,采用多线程方式同时采集不同板块数据:
threads = [] t1 =threading.Thread(target = getIndustrySection) #行业板块 t2 =threading.Thread(target = getConceptSection) #概念板块 t3 =threading.Thread(target = getAreaSection) #地域板块 threads.append(t1) threads.append(t2) threads.append(t3) if __name__ =='__main__': time1= time.time() try: for t in threads: t.setDaemon(True) t.start() while len(threads) > 0 : t = threads[0] t.join() del threads[0] except Exception as err: __logger.error(">>>>>> Exception: " +str(err)) time2= time.time() print "总用时:%d" % (time2-time1) __logger.info("总用时:%d" % (time2-time1))
5、数据分析
抓到的数据就可以用作分析了,例如1月7日板块现金流的情况(当然还要结合其他数据):main为主力流入数据,private为主力流出数据,1月7日当天只开盘半个小时即熔断,只有煤化工领域录得小幅资金流入:
6、优点和缺点
(1)优点:大多数Web数据都可以采用这种方式获得;
(2)缺点:运行效率较低,加载页面速度慢,如果数据量较大,需要长时间运行,如果是服务端linux,因为没有显示桌面,无法加载firefox或chrome驱动。