爬虫是在没有(用)API获取数据的情况下以Hack的方式获取数据的一种有效手段;进阶,就是从爬取简单页面逐渐过渡到复杂页面的过程。针对特定需求,爬取的网站类型不同,可以使用不同的python库相结合,达到快速抓取数据的目的。但是无论使用什么库,第一步分析目标网页的页面元素发现抓取规律总是必不可少的:有些爬虫是通过访问固定url前缀拼接不同的后缀进行循环抓取,有些是通过一个起始url作为种子url继而获取更多的目标url递归抓取;有些网页是静态数据可以直接获取,有些网页是js渲染数据需要构造二次请求……如果统统都写下来,一篇文章是不够的,这里举几个典型的栗子:
以从OPENISBN网站抓取图书分类信息为例,我有一批图书需要入库,但是图书信息不全,比如缺少图书分类,此时需要去openisbn.com网站根据ISBN号获取图书的分类信息。如《失控》这本书, ISBN: 7513300712 ,对应url为 http://openisbn.com/isbn/7513300712/,分析url规律就是以http://openisbn.com/isbn/作为固定前缀然后拼接ISBN号得到;然后分析页面元素作为固定前缀然后拼接ISBN号得到;然后分析页面元素,Chrome右键 —> 检查:直接使用urllib2 + re 来获得“Category:” 信息:
import re import urllib2 isbn = '7513300712' url = ' category_pattern = re.compile(r'Category: *.*, ') html = urllib2.urlopen(url).read() category_info = category_pattern.findall(html) if len(category_info) > 0 : print category_info[0] else: print 'get category failed.'
输出:
Category: 现当代小说, 小说,
2.选择合适的定位元素:
由于页面中只有一行“Category:” 信息,正则表达式提取就行,如果有很多行的话就需要缩小查找范围了,BeautifulSoup库就可以用来定位查找范围。通过分析可知,包含所需“Category:” 最近一层的div 是 <div class=“PostContent”>,仔细观察,外层还有一个 <div class=“PostContent”>,而 <div class=“Post”> 也是一样,这样如果使用它们来定位范围的话,使用find方法返回的tag对象是最先找到的外层div,范围不够小;使用findAll,返回的tag对象列表还需要遍历,综合得出用<div class=“Article”> 作为定位元素,find方法定位返回的范围够小,又不需要对find结果进行遍历。
使用urllib2 + Beautiful Soup 3 + re 再来提取一次 (Beautiful Soup最新版本为4.4,兼容python3和python2,BS4跟BS3在导包方式上有点差别):
import re,urllib2 from bs4 import BeautifulSoup isbn = '7513300712' url = 'http://openisbn.com/isbn/{0}/'.format(isbn) category_pattern = re.compile(r'Category: *.*, ') html = urllib2.urlopen(url).read() soup = BeautifulSoup(html) div_tag = soup.find('div',{'class':'Article'}) category_info = category_pattern.findall(str(div_tag)) if len(category_info) > 0 : print category_info[0] else: print 'get category failed.'
输出:
Category: 现当代小说, 小说,
3. 抓取js渲染的内容:
用baidu搜索日历,获取结果页中的节假日日期
像上次一样直接使用urllib打开网页,发现返回的html中并没有期望得到的内容,原因是我通过浏览器所看到的页面内容实际是等js渲染完成后最终展现的,中间还包含了多次的ajax请求,这样使用urllib一次就不能胜任了,此时就可以让selenium上场了(webdriver用的phantomjs,需要提前下载phantomjs放到当前的PATH路径下),由于要查找的标识 <div class=“op-calendar-new-relative”> 包含了多个,所以这次使用的方法是findAll,然后再对返回的结果进行遍历,解析每个tag对象的a属性,如果包含了“休”字标识,那么这一天就是节假日。
import re import urllib from selenium import webdriver from BeautifulSoup import BeautifulSoup holiday_list = [] url = 'http://www.baidu.com/s?' + urllib.urlencode({'wd': '日历'}) date_pattern = re.compile(r'date="[\d]+[-][\d]+[-][\d]+"') driver = webdriver.PhantomJS() driver.get(url) html = driver.page_source driver.quit() soup = BeautifulSoup(html) td_div_list = soup.findAll('div',{'class':'op-calendar-new-relative'}) for td_tag in td_div_list: href_tag = str(td_tag.a) if href_tag.find('休') != -1: holiday_date_list = date_pattern.findall(href_tag) if len(holiday_date_list) > 0: holiday_list.append(holiday_date_list[0].split('"')[1]) print holiday_list
输出:
['2016-4-2', '2016-4-3', '2016-4-4', '2016-4-30', '2016-5-1’]
4. 设置代理,抓取google play排行榜
selenium不仅可以很好的模拟浏览器行为,还可以将网页内容截图保存
from selenium import webdriver url = 'https://play.google.com/store/apps/top?hl=zh_CN' proxy_setting = ['--proxy=127.0.0.1:10800', '--proxy-type=socks5'] driver = webdriver.PhantomJS(service_args=proxy_setting) driver.get(url) driver.maximize_window() # driver.implicitly_wait(10) top_group_list = driver.find_elements_by_css_selector('.id-cluster-container.cluster-container.cards-transition-enabled') driver.get_screenshot_as_file('top.jpg’) for top_group in top_group_list: group_name = top_group.find_element_by_xpath('div/div[@class="cluster-heading"]/h2/a').text for item in top_group.find_elements_by_class_name('title'): print u'bound: {0} app: {1}'.format(group_name,item.text) driver.quit()
原文链接:https://www.zhihu.com/question/35461941/answer/99930298