时间:2023-10-12 10:48:01 | 来源:网站运营
时间:2023-10-12 10:48:01 来源:网站运营
网络爬虫系列 静态网页与动态网页的简单实战:conda install requests
安装BeautifulSoupconda install bs4
安装Seleniumconda install selenium
import requestsfrom bs4 import BeautifulSoupimport numpy as npimport pandas as pd
使用.get()函数获得网站资源,显然这里使用了http访问方法中的get方法。服务器返回代码200,表示http请求-响应成功,我们已经获得网页数据。url = 'http://detail.zol.com.cn/cell_phone_index/subcate57_0_list_1_0_1_2_0_1.html'data = requests.get(url, timeout=1) # timeout设置等待时间,超时未收到响应的话则返回连接失败data
<Response [200]>
requests会根据获得的数据来推测网页的字符编码,但这未必准确。data.encoding # 查看网页编码
'GBK'
#data.encoding = 'UTF-8' #如果通过.text得到的字符串出现乱码,则需要用此行代码重新编码。正确的网页字符编码可以在HTML的head里查看。d_text = data.textd_text[:2000]
'<!DOCTYPE html>/r/n<html>/r/n<head>/r/n<meta charset="GBK" />/n/t<title>【最热门手机大全】最热门手机报价及图片大全-ZOL中关村在线</title>/n/t<meta name="keywords" content="最热门手机,最热门手机报价,最热门手机价格,手机大全,手机最新报价" />/n/t<meta name="description" content="ZOL中关村在线提供最热门手机最新价格及经销商报价,包括最热门手机大全,最热门手机参数,最热门手机评测,最热门手机图片,最热门手机论坛等详细内容,为您购买最热门手机提供全面参考" />/n<meta http-equiv="Cache-Control" content="no-siteapp"/>/n/t<meta http-equiv="Cache-Control" content="no-transform"/>/n/t<meta name="applicable-device" content="pc">/n/t<meta name="referrer" content="unsafe-url">/n/t<meta http-equiv="mobile-agent" content="format=xhtml; url=https://wap.zol.com.cn/list/57.html?j=simple"/>/n<meta http-equiv="mobile-agent" content="format=html5; url=https://wap.zol.com.cn/list/57.html"/>/n<meta name="mobile-agent" content="format=wml;url=https://wap.zol.com.cn/list/57.html"/>/n<meta name="mobile-agent" content="format=xhtml;url=https://wap.zol.com.cn/list/57.html"/>/n<link rel="alternate" media="only screen and(max-width:640px)" href="https://wap.zol.com.cn/list/57.html"/>/n<script>(function(){var fromVal=document.location.href.indexOf("from=jinritoutiao") > -1 ? "?from=jinritoutiao" : "";var a=1,d="https://wap.zol.com.cn/list/57.html"+fromVal,b=document.cookie,c=document.referrer;b&&b.match(/mobile_agent_global_dapter=([^;$]+)/)&&(a=b.match(/mobile_agent_global_dapter=([^;$]+)/)[1]);if(1===a&&""!==d&&(/AppleWebKit.*Mobile/i.test(navigator.userAgent)||/MIDP|SymbianOS|NOKIA|SAMSUNG|LG|NEC|TCL|Alcatel|BIRD|DBTEL|Dopod|PHILIPS|HAIER|LENOVO|MOT-|Nokia|SonyEricsson|SIE-|Amoi|ZTE|TAH-AN00m/.test(navigator.userAgent))&&0>window.location.href.indexOf("?via=")){a=new Date;c=""===c?"none":-1<c.indexOf("?")?c+"&pcjump=1":c+"?pcjump=1";b&&(a.setDate(a.getDate()+1),document.cookie="PC2MRealRef="+escape(c)+";expires="+a.toGMTString()+/r/n"; domain=.zol.com.cn; path=/");try{/Android|Windows Phone|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)&&(window.location.href=d)}catch(e){}}})();</script>/n<script type="text/javascript" src="//icon.zol-img.com.cn/swfobject.'
d_text里的数据只是字符串格式,BeautifulSoup不能直接解析。首先转换成BeautifulSoup对象,它是一个复杂的树形结构,就像开发者工具里看到的层次分明的HTML。soup = BeautifulSoup(d_text)type(soup)
bs4.BeautifulSoup
下面开始提取我们需要的数据。可用.find(), .find_all(), .select()。以.select()为例,它是一个CSS选择器,根据HTML的标签、属性、层级关系来定位我们需要的数据。phone_name = soup.select('div.content > div.pic-mode-box > ul.clearfix > li > h3 > a') # 根据HTML的层次结构定位所需要的数据phone_name[:5]
[<a href="/cell_phone/index1345834.shtml" target="_blank" title="vivo X60 Pro+(8GB/128GB/全网通/5G版)微云台双主摄,骁龙888,蔡司联合影像系统">vivo X60 Pro+(8GB/128GB/全网通/5G版) <span>微云台双主摄,骁龙888,蔡司联合影像系统</span></a>, <a href="/cell_phone/index1337045.shtml" target="_blank" title="OPPO Reno5 Pro(8GB/128GB/全网通/5G版)闪电启动,星钻光芒设计,6400万水光人像四摄">OPPO Reno5 Pro(8GB/128GB/全网通/5G版) <span>闪电启动,星钻光芒设计,6400万水光人像四摄</span></a>, <a href="/cell_phone/index1337287.shtml" target="_blank" title="华为Mate40 Pro(8GB/256GB/全网通/5G版/玻璃版)麒麟9000,88°超曲环幕屏,66W快充,50W无线快充,IP68级防水防尘,3D人脸识别">华为Mate40 Pro(8GB/256GB/全网通/5G版/玻璃版) <span>麒麟9000,88°超曲环幕屏,66W快充,50W无线快充,IP68级防水防尘,3D人脸识别</span></a>, <a href="/cell_phone/index1345628.shtml" target="_blank" title="三星Galaxy S21 Ultra(12GB/256GB/全网通/5G版)骁龙888,1.08亿像素,120Hz护目屏,支持Spen">三星Galaxy S21 Ultra(12GB/256GB/全网通/5G版) <span>骁龙888,1.08亿像素,120Hz护目屏,支持Spen</span></a>, <a href="/cell_phone/index1330551.shtml" target="_blank" title="三星Galaxy Note 20 Ultra(12GB/256GB/全网通/5G版)S Pen,三星笔记,120Hz自适应屏幕,专业视频拍摄">三星Galaxy Note 20 Ultra(12GB/256GB/全网通/5G版) <span>S Pen,三星笔记,120Hz自适应屏幕,专业视频拍摄</span></a>]
代码至此,我们已经很接近得到最终的数据了。从上述的结果来看,手机信息就藏在a标签里面,下面使用.get_text()函数提取出来。for i in phone_name[:5]: name = i.get_text() # .get.text()函数可以获取标签里的文本 print(name)
vivo X60 Pro+(8GB/128GB/全网通/5G版) 微云台双主摄,骁龙888,蔡司联合影像系统OPPO Reno5 Pro(8GB/128GB/全网通/5G版) 闪电启动,星钻光芒设计,6400万水光人像四摄华为Mate40 Pro(8GB/256GB/全网通/5G版/玻璃版) 麒麟9000,88°超曲环幕屏,66W快充,50W无线快充,IP68级防水防尘,3D人脸识别三星Galaxy S21 Ultra(12GB/256GB/全网通/5G版) 骁龙888,1.08亿像素,120Hz护目屏,支持Spen三星Galaxy Note 20 Ultra(12GB/256GB/全网通/5G版) S Pen,三星笔记,120Hz自适应屏幕,专业视频拍摄
我们已经知道如何利用.get_text()函数可以获得数据,所以到了爬虫的最后一步:存储数据。这一步我们使用pandas第三方库。phone_info = pd.DataFrame(columns=['name'])phone_info['name'] = [i.get_text() for i in phone_name]phone_info.head()
name0 vivo X60 Pro+(8GB/128GB/全网通/5G版) 微云台双主摄,骁龙888,...1 OPPO Reno5 Pro(8GB/128GB/全网通/5G版) 闪电启动,星钻光芒设计,...2 华为Mate40 Pro(8GB/256GB/全网通/5G版/玻璃版) 麒麟9000,88°...3 三星Galaxy S21 Ultra(12GB/256GB/全网通/5G版) 骁龙888,1...4 三星Galaxy Note 20 Ultra(12GB/256GB/全网通/5G版) S P...
以csv格式保存数据至当前目录。此处注意编码问题。phone_info.to_csv('phone_info.csv', encoding='utf_8_sig')
以上是一个简单又完整的爬虫例子,后面的代码都是基于这个思路编写,大家读懂了上述内容的话,相信接下来的内容也能很快看懂。def multi_url(): phone_dataset = [] # 存储数据 for num in range(1, 3): # 改变地址的特定部分 u = f'http://detail.zol.com.cn/cell_phone_index/subcate57_0_list_1_0_1_2_0_{num}.html' # 检查地址是否正确 try: data = requests.get(u, timeout=1) data.encoding # 理论上,若URL错误,则无法获得网页资源,从而无法进行encoding,程序会报错 except: print('已到达最后一页!') return phone_dataset d_text = data.text # 采集当前页面的数据 soup = BeautifulSoup(d_text) phone_name = soup.select('div.content > div.pic-mode-box > ul.clearfix > li > h3 > a') # 储存 for j in phone_name: phone_dataset.append(j.get_text()) return phone_datasetphone2 = multi_url()print(phone2[-3:])
['华为nova 7 SE(8GB/128GB/5G版/全网通) 麒麟820,6400万高清AI四摄,40W超级快充', '华为nova 5 Pro(8GB/128GB/全网通) 3200万超高清自拍,人像超级夜景', '荣耀30S(8GB/128GB/全网通/5G版) 5G双模,麒麟820,40W快充']
def multi_next_page(): phone_dataset = [] # 存储数据 # 采集第一页数据 data = requests.get('http://detail.zol.com.cn/cell_phone_index/subcate57_0_list_1_0_1_2_0_1.html').text soup = BeautifulSoup(data) phone_name = soup.select('div.content > div.pic-mode-box > ul.clearfix > li > h3 > a') for i in phone_name: phone_dataset.append(i.get_text()) # 开始多页循环 base_url = 'http://detail.zol.com.cn/' num = 1 while num < 2: try: p = soup.select('a.next') # 定位到“下一页”按钮 page = p[0].get('href') # 通过.get('herf')来获得下一页的部分地址 num += 1 print(f'第{num}页采集ing...') except: print('采集完成!') return phone_dataset url = base_url + str(page) # 合并成完整的地址 # 采集数据 data = requests.get(url).text soup = BeautifulSoup(data) phone_name = soup.select('div.content > div.pic-mode-box > ul.clearfix > li > h3 > a') for i in phone_name: phone_dataset.append(i.get_text()) return phone_datasetphone3 = multi_next_page()print(phone3[-3:])
['华为nova 7 SE(8GB/128GB/5G版/全网通) 麒麟820,6400万高清AI四摄,40W超级快充', '华为nova 5 Pro(8GB/128GB/全网通) 3200万超高清自拍,人像超级夜景', '荣耀30S(8GB/128GB/全网通/5G版) 5G双模,麒麟820,40W快充']
注意到,我们找到的Request URL,与茅台的原始地址是完全不一样的。
url = r'http://you.163.com/xhr/comment/listByItemByTag.json?__timestamp=1612421786209&itemId=3995885&tag=%E5%85%A8%E9%83%A8&size=20&page=1&orderBy=0&oldItemTag=%E5%85%A8%E9%83%A8&oldItemOrderBy=0&tagChanged=0'data = requests.get(url)d_text = data.textd_text[:2000]
'{"code":"200","data":{"pagination":{"page":1,"size":20,"totalPage":1288,"total":25748,"lastPage":false},"commentList":[{"skuInfo":["规格:1瓶"],"frontUserName":"小****","frontUserAvatar":"https://yanxuan.nosdn.127.net/4719d50913c28fac5aa575ba6f21fe20","content":"物流非常快,质量非常好,客服态度非常好,相信我自己还能再中一次!","createTime":1603867171787,"picList":["https://yanxuan.nosdn.127.net/05b30e92fd11ddef6decdded5c674253.jpg","https://yanxuan.nosdn.127.net/110e362866760ce9b0d7ccdee76f4bfe.jpg"],"commentReplyVO":null,"memberLevel":5,"appendCommentVO":{"id":143233162,"createTime":1603867470466,"content":"很有品质保证,买买买!!!","picList":["https://yanxuan.nosdn.127.net/8e63dc16f7be996cafbb8094fd039f1a.jpg","https://yanxuan.nosdn.127.net/66825d8d503f08074fa1ef7fd88a14f3.jpg"],"commentReplyVO":null},"star":5,"itemId":3995885},{"skuInfo":["规格:1瓶"],"frontUserName":"用****8","frontUserAvatar":"https://yanxuan.nosdn.127.net/9e6dadfc9eb0954061797dfd88f13980.jpg","content":"终于抢到一瓶,感谢网易严选!希望能再给我中一瓶!","createTime":1605188815733,"picList":["https://yanxuan.nosdn.127.net/8bfe48109701139745ac8c6018bf38f5.jpg"],"commentReplyVO":null,"memberLevel":4,"appendCommentVO":null,"star":5,"itemId":3995885},{"skuInfo":["规格:1瓶"],"frontUserName":"r****0","frontUserAvatar":null,"content":"终于让我抢到一瓶,太好了,抢不到只能从黄牛手里拿了","createTime":1606895108114,"picList":["https://yanxuan.nosdn.127.net/f94f2a8d0689622d5391aa7b29371522.jpg"],"commentReplyVO":null,"memberLevel":5,"appendCommentVO":null,"star":5,"itemId":3995885},{"skuInfo":["规格:1瓶"],"frontUserName":"1****3","frontUserAvatar":"http://yanxuan.nosdn.127.net/bb31a487ad26923bcf115f896e789ebf","content":"速度很快,酒不错,包装很到位,很好,网易严选,很好,产地直接发货,厉害了,支持","createTime":1603806876726,"picList":["https://yanxuan.nosdn.127.net/0dfc65a5b6b475175600f9f7908483ef.jpg","https://yanxuan.nosdn.127.net/d8d638707f3541da4205b9a4af4ea072.jpg","https://yanxuan.nosdn.127.net/f71bbb359b0f59bd28c96792836e5869.jpg","https://yanxuan.nosdn.127.net/e73fb3856d31edcd2f8c25ffe9465427.jpg","https://yanxuan.nosdn.127.net/97266996534f'
import jsonjson_data = json.loads(d_text)type(json_data)
dict
这里的json数据存在复杂的字典与列表嵌套(大家可以自行print出来观察),肉眼不容易看清楚嵌套关系,但开发者工具提供了良好的可视化(Network——Preview),正如上一幅图片所示。根据其中的嵌套逻辑,我们先找到了第一条评论。comment_list = json_data['data']['commentList'][0]['content']comment_list
从上一幅图片看到,data隔离有一个大括号 { ,因此这一层是字典;而 0 隔壁有一个中括号 [ ,所以这层是列表。以此类推。
'物流非常快,质量非常好,客服态度非常好,相信我自己还能再中一次!'
在开发者工具里对比观察第一、第二条评论的索引,我们发现'data''commentList''content'这三个索引值是固定不变的,只有0需要递增到1,因此我们只需要运用循环语句,改变第三个索引值,就能获得其余的评价内容。同时注意单页的评价数,下面提供两种方法,推荐使用第二条语句。len(json_data['data']['commentList']) # 使用函数计算本页评价的数量json_data['data']['pagination']['size'] # json数据也交代了该页的评价数,可以直接索引获取
comment_list = [json_data['data']['commentList'][i]['content'] for i in range(20)]comment_list
['物流非常快,质量非常好,客服态度非常好,相信我自己还能再中一次!', '终于抢到一瓶,感谢网易严选!希望能再给我中一瓶!', '终于让我抢到一瓶,太好了,抢不到只能从黄牛手里拿了', '速度很快,酒不错,包装很到位,很好,网易严选,很好,产地直接发货,厉害了,支持', '好不容易抢到一瓶,真心不容易。但是也非常幸运吧。网易几年的老用户了,东西都不错,价格也便宜。没啥理由不选网易严选的东西。以后还会一如既往地选择严选。', '几个平台只有网易抢到了,包装完美,有严选的溯源码是正品,感谢三石!!再接再厉再来一瓶!!', '严选还有专门的溯源标签,点赞,飞天真香呀,顺丰超快,开森^_^', '贴严选标签了,看着很好,舍不得拆开看里面的酒,应该没问题。希望还能再买到', '物流很快,隔天就到了,酒完美无瑕疵,还会回购的', '酒收到了、包装完美、货真价实。感谢网易这么好的购物平台,晚上就和朋友们喝了去', '第1次喝到平价的茅台,平常喝的都是3000多一瓶。迫不及待与好友共饮,大家合作都比较愉快。好酒!', '努力了几天抢到了,这么多年老用户了不让我买到,我会心寒的,不错,速度很快,包装很妥帖,有海绵垫瓶盖也有海绵垫,防护工作做的很好,家里有几样东西常年用严选的,加把劲再让我抢一瓶,这才对得起我的支持 ', '包装太好了,太精美了,网易云县良心良心。以后购物就在网易严选呐。', '完美,很开心能抢到,感谢平台,平台的服务也很好', '不错不错!几天努力终于抢到了了,快递速度很快,产品保护周全,安全收到了,各方面都很好,好评!', '网易严选非常靠谱,值得信赖!多年的客户了,酱香经典、茅台最醇!非常不错,留着过年和家人一起喝,感谢网易严选!', '发货速度非常快,第二天就到了,验看了一下,包装很好,是正品,值得购买。', '非常漂亮的茅台酒,网易严选必出精品,超级喜欢,物流也非常不错,还要继续购买', '包装很好,酒品很好。非常完美', '未填写评价内容']
from selenium import webdriverfrom selenium.webdriver.common.action_chains import ActionChainsimport time
selenium依赖浏览器程序而运行,因此第一步就需要给它定义一个浏览器。运行本行代码会弹出浏览器窗口。文件夹里提供一个微型的88.0版本谷歌浏览器,当然大家也可以用自己喜欢的浏览器。driver = webdriver.Chrome(executable_path='./chromedriver.exe')driver.get(r'http://you.163.com/item/detail?id=3995885&_stat_area=mod_16_item_16&_stat_id=1005002&_stat_referer=itemList')time.sleep(2) # 相当于给代码运行添加一个空白过程,便于等待页面加载
商品的首页不会出现评论,需要点击“评论”按钮,等待评论内容加载后才能爬取。这就需要让selenium模拟真人进行鼠标操作。button = driver.find_element_by_css_selector('li.item-1') # 通过CSS选择器定位“评论”按钮action = ActionChains(driver).move_to_element(button) # 模拟将鼠标悬停到按钮上action.click(button).perform() # .click()即点击,perform()表示执行上述所有动作链time.sleep(1)
将页面拉到最底部,从而加载本页所有评论。driver.execute_script("var q=document.documentElement.scrollTop=10000")
下面解析HTML的操作与BeautifulSoup类似,selenium也可以用CSS选择器定位特定数据,然后用.text获得文本。comment_list = driver.find_elements_by_css_selector('div.content.f-breakall')comment_list[:5]
[<selenium.webdriver.remote.webelement.WebElement (session="33efbd2ce88b7c5d03394f6eaa65503c", element="6d89b995-76ef-4ffd-9b74-80a5a8e68873")>, <selenium.webdriver.remote.webelement.WebElement (session="33efbd2ce88b7c5d03394f6eaa65503c", element="44819238-ae3d-4212-b452-10eae73a6c27")>, <selenium.webdriver.remote.webelement.WebElement (session="33efbd2ce88b7c5d03394f6eaa65503c", element="df90dd8f-41a6-471f-9cde-ccf7d48cafc5")>, <selenium.webdriver.remote.webelement.WebElement (session="33efbd2ce88b7c5d03394f6eaa65503c", element="f3469893-c40e-4cfd-822f-749b86054382")>, <selenium.webdriver.remote.webelement.WebElement (session="33efbd2ce88b7c5d03394f6eaa65503c", element="58b21810-515a-4cde-8786-408d8ce73b49")>]
comment_text = [i.text for i in comment_list]comment_text[:5]
['物流非常快,质量非常好,客服态度非常好,相信我自己还能再中一次!', '[追评] 很有品质保证,买买买!!!', '终于抢到一瓶,感谢网易严选!希望能再给我中一瓶!', '终于让我抢到一瓶,太好了,抢不到只能从黄牛手里拿了', '速度很快,酒不错,包装很到位,很好,网易严选,很好,产地直接发货,厉害了,支持']
关键词:简单,实战,动态,静态,爬虫,网络,系列