导语:最近写了好几个简单的爬虫,踩了好几个深坑,在这里总结一下,给大家在编写爬虫时候能给点思路。本次爬虫内容有:静态页面的爬取。动态页面的爬取。web自动化终极爬虫。 分析:数据获取(主要靠爬虫)
数据存储(python excel存储)
数据获取实战:百度音乐(静态网页)分析步骤 2 . 打开浏览器调试模式F12,选择Network+all模式 3 . 搜索框搜索歌曲(beat it),查看控制台
4 .通过以上分析:获取到有效信息:
5 .通过有效信息来设计爬虫,获取数据
代码实现1 .View 提供准对参数url进行访问并返回结果的方法 def view(url): ''' :param url: 待爬取的url链接 :return: ''' # 从url中获取host protocol, s1 = urllib.splittype(url) host, s2 = urllib.splithost(s1) # 伪装浏览器,避免被kill headers = { 'Host': host, 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate', 'Accept-Language': 'zh-CN,zh;q=0.8', } # 代理 proxies = { "http": "dev-proxy.oa.com:8080", "https": "dev-proxy.oa.com:8080", } # 利用requests封装的get方法请求url,并添加请求头和代理,获取并返回请求结果。并进行异常处理 try: res = requests.get(url, headers=headers, proxies=proxies) res.encoding = 'utf-8' if res.status_code == 200: # 请求成功 # 获取网页内容,并返回 content = res.text return content else: return None except requests.RequestException as e: # 异常处理 print(e) return None 2 .search_baidu_song 提供对参数song_name进行歌曲搜索并获取搜索结果 def search_baidu_song(song_name): ''' 获取百度音乐搜索歌曲结果 :param song_name: 待搜索歌曲名 :return: 搜索结果 ''' def analyse(): ''' 静态网页分析,利用BeautifulSoup,轻松获取想要的数据。需要对html有了解。 :return: ''' # 初始化BeautifulSoup对象,并指定解析器为 lxml。还有其他的解析器:html.parser、html5lib等 # 详细教程可访问:http://cuiqingcai.com/1319.html《Python爬虫利器二之Beautiful Soup的用法》 html = BeautifulSoup(content, "lxml") # beautifulsoupzui常用方法之一: find_all( name , attrs , recursive , text , **kwargs ) # find_all() 方法搜索当前tag的所有tag子节点, 并判断是否符合过滤器的条件 # tag标签名为'div'的并且标签类名为class_参数(可为 str、 list、 tuple), search_result_divs = html.find_all('div', class_=['song-item clearfix ', 'song-item clearfix yyr-song']) for div in search_result_divs: # find() 方法搜索当前tag的所有tag子节点, 并返回符合过滤器的条件的第一个结点对象 song_name_str = div.find('span', class_='song-title') singer = div.find('span', class_='singer') album = div.find('span', class_='album-title') # 此部分需要对html页面进行分析,一层层剥开有用数据并提取出来 if song_name_str: # 获取结点对象内容,并清洗 song_name_str = song_name.text.strip() else: song_name_str = '' if singer: singer = singer.text.strip() else: singer = '' if album: album = album.find('a') if album: # 获取标签属性值 # 方法二:属性值 = album['属性名'] album = album.attrs.get('title') if album and album != '': album = album.strip() else: album = '' else: album = '' # print song_name + " | " + singer + " | " + album songInfoList.append(SongInfo(song_name_str, singer, album)) songInfoList = [] url = urls.get('baidu_song') url1 = url.format(song_name=song_name, start_idx=0) content = self.view(url1) if not content: return [] analyse(content) url2 = url.format(song_name=song_name, start_idx=20) content = self.view(url2) analyse(content) return songInfoList[0:30] 就这样我们获取到了百度网页歌曲搜索结果的数据。然后就是保存数据,这个我们最后再谈谈。 网易云音乐 (动态网页)在我们以上一种静态网页获取数据方式来获取网易云音乐的数据的时候,可能会遇到这样的问题:网页查看源代码并没有可用的数据,仅仅只有网页的骨架。数据完全找不到,可是打开开发者工具查看DOM树却能找到想要的数据,这时候我们是遇到了动态网页,数据是在动态加载进去的。无法获取网页数据。
方案一实现(通过查看访问动态数据接口来获取数据):
过滤请求为XHR,发现请求name怎么都一样,这时候我们翻看这些name,查看到Request URL里找到关键字search的请求,这个请求是一个POST请求。这个应该就是获取搜索数据的接口,通过查看response或者preview来查看请求返回结果。正是我们想要的。 我们先不要高兴的太早了,目前我们还没有搞清楚Form Data是怎么构成的。params + encSecKey到底是怎么生成的。在看过网络上有关抓取网易评论的爬虫《如何爬网易云音乐的评论数?》,得知这个网易针对api做了加密处理。由于个人道行太浅参悟不透这里的加密参数顺序和内容。因此这个方案就此作罢。实在不甘心,只好换方案二。 方案二实现:既然方案一暂时走不通,也不能影响我们的工作进度,换个思路继续走,想到使用web自动化测试工具selenium可以实现模拟人工操纵浏览器。这样导出网页数据应该不是问题,想到立马动手。 环境配置
2 .安装PhantomJS
1.安装PhantomJS 下载完成后解压文件,可将phantomjs.exe放在pythond的目录下(C:\Python27\phantomjs.exe)。这样后续加载不需要指定目录。也可以放在特定目录,使用的时候指定phantomjs.exe路径即可。双击打开phantomjs.exe验证安装是否成功。如果出现下图,即安装成功了。 2.代码步骤实现:
def dynamic_view(url): ''' 使用自动化工具获取网页数据 :param url: 待获取网页url :return: 页面数据 ''' # 初始化浏览器driver driver = webdriver.PhantomJS() # 浏览器driver访问url driver.get(url) # 坑:不同frame间的转换(网易云在数据展示中会将数据动态添加到'g_iframe'这个框架中,如果不切换,会报"元素不存在"错误。) driver.switch_to.frame("g_iframe") # 隐式等待5秒,可以自己调节 driver.implicitly_wait(5) # 设置10秒页面超时返回,类似于requests.get()的timeout选项,driver.get()没有timeout选项 driver.set_page_load_timeout(10) # 获取网页资源(获取到的是网页所有数据) html = driver.page_source # 坑:退出浏览器driver,必须手动退出driver。 driver.quit() # 返回网页资源 return html
def search_163_song(song_name): pass 同样是通过BeautifulSoup对网页资源进行对象化,在通过对对象的筛选获取得到数据。没想到网易云音乐的数据也能这样拿到。能做到这里已经可以对付大部分网站了。 选用PhantomJS看中其不需要可视化页面,在内存占用上比较省。可是也是出现问题,各位看官请继续往下看。眼看着就要完成了。 3. spotify
解决方案:
方案实施:方案1: 采用web自动化工具获取数据:配置如同网易云配置,模仿用户操作浏览器进行网页打开,用户登录,进入搜索页面,获取页面数据 def spotify_view(url): ''' 使用自动化工具获取网页数据 :param url: 待获取网页url :return: 页面数据 ''' spotify_name = 'manaxiaomeimei' spotify_pass = 'dajiagongyong' spotify_login = 'https://accounts./en/login' # 初始化浏览器driver driver = webdriver.PhantomJS() # 模拟用户登录() # 浏览器driver访问登录url driver.get(spotify_login) # 休息一下等待网页加载。(还有另一种方式:driver.implicitly_wait(3)) time.sleep(3) # 获取页面元素对象方法(本次使用如下): # find_element_by_id : 通过标签id获取元素对象 可在页面中获取到唯一一个元素,因为在html规范中。一个DOM树中标签id不能重复 # find_element_by_class_name : 通过标签类名获取元素对象,可能会重复(有坑) # find_element_by_xpath : 通过标签xpath获取元素对象,类同id,可获取唯一一个元素。 # 获取页面元素对象--用户名 username = driver.find_element_by_id('login-username') # username.clear() # 坑:获取页面元素对象--密码 # 在通过类名获取标签元素中,遇到了无法定位复合样式,这时候可采用仅选取最后一个使用的样式作为参数,即可(稳定性不好不建议使用。尽量使用by_id) # password = driver.find_element_by_class_name('form-control input-with-feedback ng-dirty ng-valid-parse ng-touched ng-empty ng-invalid ng-invalid-required') password = driver.find_element_by_class_name('ng-invalid-required') # password.clear() # 获取页面元素对象--登录按钮 login_button = driver.find_element_by_xpath('/html/body/div[2]/div/form/div[3]/div[2]/button') # 通过WebDriver API调用模拟键盘的输入用户名 username.send_keys(spotify_name) # 通过WebDriver API调用模拟键盘的输入密码 password.send_keys(spotify_pass) # 通过WebDriver API调用模拟鼠标的点击操作,进行登录 login_button.click() # 休息一下等待网页加载 driver.implicitly_wait(3) # 搜索打开歌曲url driver.get(url) time.sleep(5) # 搜索获取网页代码 html = driver.page_source return html
点击运行之后,一切都风平浪静。突然代码报错了(如下图)。查完资料也做了代码的修改。 网络提供方案
方案实施: 方案1: 在获取了对象之后添加对该对象的清除方法(username.clear()、password.clear()) 实施结果 方案1失败。原因不明了,多半是webdriver对PhantomJS兼容性不好。 方案2: 更换浏览器,本次选择使用chrome浏览器进行自动化操作。 安装chrome自动化控制插件。
本以为这样就可以获取到数据了。燃鹅,还是没有获取到,又报错了(如下图) 到这里:就应该查看请求了,找到token是什么。并尝试添加token到请求头中。 查看cookies 可是在我们登录后的cookies列表中却没有这个cookie! 预测这个cookie应该是在web播放器加载时种下的。验证一下: 由上表可知。该token在加载播放器的时候种下的。 到这里问题,解决一大半了。 |
|