前言:
本次爬取的目标采用的Ajax方式加载页面信息,并且这些Ajax的接口参数比较复杂,想要构造Ajax参数模拟请求比较困难。对于这种页面,最方便快捷的抓取方法就是通过Selenium。使用Selenium来模拟浏览器操作,来抓取京东的商品信息,并将最后的结果保存至MongoDB中。
1. 本次目标
本节中,我们要利用Selenium抓取京东商品并用Beautifu Soup解析得到商品的图片、名称、价格、店铺名称和评论数,并将其保存到MongoDB。
2. 准备工作
本次爬取使用的是Chrome浏览器,所以需要安装好Chrome浏览器并配置好ChromeDriver;另外,还需要正确安装Python的Selenium库,Beautifu Soup解析库和MongoBD即可。下面部分代码可能会省去导入相应包的步骤。
详细的安装方式在这里就不赘述。
3. 接口分析
编写代码之前,首先需要分析一下网页。
打开京东页面,搜索商品,比如手机,此时打开开发者工具,截获Ajax请求,我们可以发现获取商品列表的接口,如图一所示。
图一
这里可以看到请求的URL地址里的show_items参数非常复杂并且没有规律(这里笔者查看了几个页面,没有发现规律,大致猜测为商品的编号),想要构造出Ajax的参数来模拟请求非常困难,所以如果直接用Selenium来模拟浏览器的话,就不需要再关注这些接口参数了,只要在浏览器里面可以看到的,都可以爬取。这也就是获取这类页面选择使用Selenium的原因。
4、获取页面
想要爬取京东上某一商品,比如手机,就需要获取所有包含手机的页面,下面就来分析页面的URL有哪些规律。
图二
图三
图二是在京东首页搜索手机后得到的页面,图三是点击下一页之后得到的页面。可以看到其主要的参数有三个:keyword,enc,page。
其中,keyword就是搜索的关键字如手机;enc是编码方式如utf-8(如果这里不加这个参数搜索出来会出现编码错误,如图四);page是页面参数,这里page=3,实际页面是第二页,易得到page和实际页数(n)的关系是:page = 2n(实际页数) -1。京东最大页数为100页,即n的最大值为100。
图四
那么只需要构建出一个get_url函数,其中URL带有以上三个参数,n在从range(1,101)全部遍历一遍就能得到全部URL了。代码实现如下:
1 def get_url(): 2 for n in range(1,101): 3 keyword = {\'keyword\': 手机} 4 # 页面n与参数page的关系 5 page = \'&page=%s\' % (2 * n - 1) 6 # 使用urlencode将keyword转码成可识别的url格式,enc以utf-8方式编码,并得传入 7 对应page,得到完整的url 8 url = \'https://search.jd.com/Search?\' + parse.urlencode(keyword) + 9 \'&enc=utf-8\' + page 10 return url
5、获取商品
获取所有页面后,接下来就要从页面中提取所需要的商品信息。首先分析一下页面,可以看到所有的商品都在id=‘J_goodsList’的div标签下,标签里面的每一个class="gl-item"的li标签就是每一个商品,如图五,点开li标签,里面包含的就是商品的图片、商品名、价格、评论数、店铺等所有信息,如图六。
图五
图六
找到相关信息的位置,下面只需要用Beautiful Soup将相关信息解析出来即可,下面以商品价格为例。
图七
如图七,可以看到商品的价格在class=‘p-price’的div标签里面的em标签和i标签中,使用Beautiful Soup解析,代码实现如下:
1 # html为页面的html源代码 2 html = browser.page_source 3 soup = BeautifulSoup(html, \'lxml\') 4 # 找到所有商品标签 5 goods = soup.find_all(\'li\', class_="gl-item") 6 # 遍历每个商品,得到每个商品的信息 7 for good in goods: 8 tag = good.find(\'div\', class_="p-price").strong.em.string 9 money = good.find(\'div\', class_="p-price").strong.i.string
这样得到的tag就为¥,money就为商品的价格数值,最后商品的价格为price = tag + money。同样的方式可以得到其他信息,代码实现如下:
1 html = browser.page_source 2 soup = BeautifulSoup(html, \'lxml\') 3 # 找到所有商品标签 4 goods = soup.find_all(\'li\', class_="gl-item") 5 # 遍历每个商品,得到每个商品的信息 6 for good in goods: 7 tag = good.find(\'div\', class_="p-price").strong.em.string 8 money = good.find(\'div\', class_="p-price").strong.i.string 9 # 这里有一个bug!就是京东有些商品竟然没有店铺名,导检索store时找不到对应的节点导致报错 10 store = good.find(\'div\', class_="p-shop").span 11 commit = good.find(\'div\', class_="p-commit").strong.a.string 12 name = good.find(\'div\', class_="p-name p-name-type-2").a.em 13 image = good.find(\'div\', class_="p-img").a.img.get(\'src\') 14 if store is not None: 15 new_store = store.a.string 16 else: 17 new_store = \'没有找到店铺 - -!\' 18 new_name = \'\' 19 for item in name.strings: # 由于name.strings生成的是一个包含多个string的生成器,所以需要遍历出所有的string,在通过字符串的拼接成完整的新字符串 20 new_name = new_name + item 21 product = { 22 \'商品名\': new_name, 23 \'价格\': tag + money, 24 \'店铺\': new_store, 25 \'评论量\': commit, 26 \'图片\': \'https://\' + image 27 }
从上述代码注释可以看出,对于获取的到部分信息,还需要做一些处理才能得到理想的数据,比如商品的店铺和商品名称还有商品的图片(关于图片的问题下文再提,这里只说明前两者)。
京东有些店铺没有店名,如图八,这样就会导致上述代码运行时无法获取到相应节点,导致出现报错。所以这里就添加了一条判断语句,用来判断是否存在该节点,即是否有店铺名,如果有就获取相应标签的字符串得到店铺名,如果没有,则赋值一条没有的提示信息。这里可以把is not None的判断写在前面,因为大部分的店铺都有店铺名的,这样可以提升代码的执行效率。
图八
关于商品名称问题可见图九,可以看到em标签下不止一个NavigableString 类型的子节点,所以需要使用.strings获取所有字符,而.strings得到的又是一个包含全部string的生成器,所以这里需要遍历出所有string再拼接成一个完整的字符。(关于.string和.strings的知识链接:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/#strings-stripped-strings)
图九
6、触发Ajax
代码写到这里其实已经可以获取到商品的信息了。但是这里存在一个问题,那就是单页获取的商品数量比实际要少。(每页获取到的是30个,实际是有60个商品)原因就是因为另一部分商品的加载使用的是Ajax,直接获取页面的html源代码是无法得到这一部分代码信息,所以这里需要使用到selenium来模拟浏览器,使其滑动滑轮至页面底部。通过这样一种方式来触发Ajax,加载出全部的商品信息。代码实现如下:
1 from selenium import webdriver 2 import time 3 url =\' https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&wq=%E6%89%8B%E6%9C%BA&pvid=20500cd8117447afbbb910f3a494b260\' 4 browser = webdriver.Chrome() 5 #这里的url为在京东上搜索手机所得到的url,暂不用过多考虑 6 browser.get(url) 7 browser.execute_script("window.scrollTo(0,document.body.scrollHeight)") 8 time.sleep(2) 9 html = browser.page_source 10 print(html)
通过第七行代码就能够实现滑轮下拉至页面底部的操作,第八行代码设置一个2s的延时等待保证页面的加载完全。
整合好之前的代码运行,以下就是抓取单页手机的结果:
1 爬取信息并保存中... 2 {\'商品名\': \'荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待\', \'价格\': \'¥1198.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'97万+\', \'图片\': \'https://done\'} 3 {\'商品名\': \'Apple iPhone X (A1865) 64GB 深空灰色 移动联通电信4G手机\', \'价格\': \'¥6999.00\', \'店铺\': \'京东Apple产品专营店\', \'评论量\': \'105万+\', \'图片\': \'https://done\'} 4 {\'商品名\': \'小米8青春版 镜面渐变AI双摄 6GB+64GB 梦幻蓝 全网通4G 双卡双待 全面屏拍照游戏智能手机\', \'价格\': \'¥1699.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'12万+\', \'图片\': \'https://done\'} 5 {\'商品名\': \'荣耀10 GT游戏加速 AIS手持夜景 6GB+64GB 幻夜黑 全网通 移动联通电信4G 双卡双待 游戏手机\', \'价格\': \'¥2198.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'86万+\', \'图片\': \'https:////img12.360buyimg.com/n7/jfs/t17149/173/1779654602/189601/335d3d90/5ad8628dN03dc292f.jpg\'} 6 {\'商品名\': \'OPPO Find X曲面全景屏 波尔多红 8GB+128GB 全网通 移动联通电信全网通4G 双卡双待手机\', \'价格\': \'¥4999.00\', \'店铺\': \'OPPO京东自营官方旗舰店\', \'评论量\': \'5.3万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t23167/95/285071867/94556/c98fa4f6/5b2b6212N106d8382.jpg\'} 7 {\'商品名\': \'华为 HUAWEI P20 AI智慧徕卡双摄全面屏游戏手机 6GB+64GB 极光色 全网通移动联通电信4G手机 双卡双待\', \'价格\': \'¥3388.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'48万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t18052/318/2334327001/256076/23da5f45/5af13917Naca6cb3d.jpg\'} 8 {\'商品名\': \'Apple iPhone 8 64GB 红色特别版 移动联通电信4G手机\', \'价格\': \'¥4699.00\', \'店铺\': \'京东Apple产品专营店\', \'评论量\': \'108万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t19342/199/1516671468/71132/8e37293d/5acc524cN424bbaa0.jpg\'} 9 {\'商品名\': \'黑鲨游戏手机 8GB+128GB 极夜黑 液冷更快 全网通4G 双卡双待\', \'价格\': \'¥2999.00\', \'店铺\': \'黑鲨京东自营官方旗舰店\', \'评论量\': \'12万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t17599/148/1789034295/271289/8bdbd5bd/5ad875d4N23049c2c.jpg\'} 10 {\'商品名\': \'华为 HUAWEI P20 Pro 全面屏徕卡三摄游戏手机 6GB+128GB 极光色 全网通移动联通电信4G手机 双卡双待\', \'价格\': \'¥4988.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'29万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t17689/292/1247919821/159809/1c87eb05/5ac1eae4Nce7c8b00.jpg\'} 11 {\'商品名\': \'vivo Z1极光特别版 新一代全面屏AI双摄手机 4GB+64GB 移动联通电信全网通4G手机\', \'价格\': \'¥1398.00\', \'店铺\': \'vivo京东自营官方旗舰店\', \'评论量\': \'41万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t1/2533/19/5800/382950/5ba0b1b7Eb550a26e/934ebb6f3f60e2e6.jpg\'} 12 {\'商品名\': \'华为 HUAWEI nova 3全面屏高清四摄游戏手机 6GB+128GB 亮黑色 全网通移动联通电信4G手机 双卡双待\', \'价格\': \'¥2799.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'16万+\', \'图片\': \'https:////img12.360buyimg.com/n7/jfs/t24199/102/894210890/231864/c3d58dbe/5b478f4aNb1f2d6bd.jpg\'} 13 {\'商品名\': \'一加手机6 8GB+128GB 亮瓷黑 全面屏双摄游戏手机 全网通4G 双卡双待\', \'价格\': \'¥3199.00\', \'店铺\': \'一加手机京东自营官方旗舰店\', \'评论量\': \'42万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t22519/185/2357218296/213888/ff5e9aa3/5b7bd314N6db24355.jpg\'} 14 {\'商品名\': \'Apple iPhone 8 Plus 64GB 红色特别版 移动联通电信4G手机\', \'价格\': \'¥5699.00\', \'店铺\': \'京东Apple产品专营店\', \'评论量\': \'151万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t17383/125/1470478028/71155/1cb53bc2/5acc5248N6a5f81cd.jpg\'} 15 {\'商品名\': \'诺基亚 NOKIA X6 6GB+64GB 星空黑 全网通 双卡双待 移动联通电信4G手机\', \'价格\': \'¥1599.00\', \'店铺\': \'诺基亚手机京东自营旗舰店\', \'评论量\': \'16万+\', \'图片\': \'https:////img12.360buyimg.com/n7/jfs/t19759/338/2338072914/190731/65ba22/5af0647bNca23c27c.jpg\'} 16 {\'商品名\': \'荣耀Note10 全网通6G+64G 幻夜黑 移动联通电信4G全面屏手机 双卡双待 游戏手机\', \'价格\': \'¥2698.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'8.1万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t20140/279/2633113298/113707/57d9da77/5b6018c5N6f80495e.jpg\'} 17 {\'商品名\': \'OPPO A5 全面屏拍照手机 4GB+64GB 幻镜粉 全网通 移动联通电信4G 双卡双待手机\', \'价格\': \'¥1499.00\', \'店铺\': \'OPPO京东自营官方旗舰店\', \'评论量\': \'7.1万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t21835/276/1962902576/82126/4e7a2eca/5b41ed07Nce2cfe60.jpg\'} 18 {\'商品名\': \'小米8SE 全面屏智能游戏拍照手机 6GB+64GB 灰色 骁龙处理器 全网通4G 双卡双待\', \'价格\': \'¥1799.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'48万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t22330/332/515182850/188708/3dbe80f8/5b0fbaabN3229c7a3.jpg\'} 19 {\'商品名\': \'荣耀 V10 高配版 6GB+64GB 幻夜黑 移动联通电信4G全面屏游戏手机 双卡双待\', \'价格\': \'¥1998.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'94万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t13441/73/1250191369/239632/8b94bbc6/5a1d1e2dN6ba9aac4.jpg\'} 20 {\'商品名\': \'荣耀8X 千元屏霸 91%屏占比 2000万AI双摄 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待\', \'价格\': \'¥1399.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'52万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t1/204/12/2599/82133/5b963c1aEd9fa390e/970adadd473ff485.jpg\'} 21 {\'商品名\': \'Apple iPhone XR (A2108) 128GB 黑色 移动联通电信4G手机 双卡双待\', \'价格\': \'¥6999.00\', \'店铺\': \'京东Apple产品专营店\', \'评论量\': \'21万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t1/3405/18/3537/69901/5b997c0aE5dc8ed9f/a2c208410ae84d1f.jpg\'} 22 {\'商品名\': \'Apple iPhone XS Max (A2104) 256GB 深空灰色 移动联通电信4G手机 双卡双待\', \'价格\': \'¥10999.00\', \'店铺\': \'京东Apple产品专营店\', \'评论量\': \'20万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t1/3/15/4536/138660/5b997bf8Ed72ebce7/819dcf182d743897.jpg\'} 23 {\'商品名\': \'【OPPO全新系列】K1 OPPO首款千元屏幕指纹手机 6G+64G 银光绿 全网通 移动联通电信4G 双卡双待手机\', \'价格\': \'¥1799.00\', \'店铺\': \'OPPO京东自营官方旗舰店\', \'评论量\': \'9.9万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t1/9723/3/3933/247634/5bd97d98Ecce0ffc6/b6907abf75afe34d.jpg\'} 24 {\'商品名\': \'小米 红米Note5 全网通版 6GB+128GB 黑色 移动联通电信4G手机 双卡双待\', \'价格\': \'¥1399.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'60万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t24202/21/1240414017/266807/216c76b7/5b5705a1N6a12c28c.jpg\'} 25 {\'商品名\': \'小米8 全面屏游戏智能手机 6GB+64GB 蓝色 全网通4G 双卡双待\', \'价格\': \'¥2499.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'78万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t20569/241/521943946/338336/409b289f/5b0fcb56N90ae22f0.jpg\'} 26 {\'商品名\': \'小米 红米6A AI美颜 3GB+32GB 流沙金 全网通4G手机 双卡双待\', \'价格\': \'¥669.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'50万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t22099/97/2250481418/237159/c4a1502d/5b4edfdcN5a7d6faf.jpg\'} 27 {\'商品名\': \'小米 红米6 4GB+64GB 流沙金 全网通4G手机 双卡双待\', \'价格\': \'¥849.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'42万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t23653/202/1047354722/252149/77642e5a/5b4ee1a1Nb44ade36.jpg\'} 28 {\'商品名\': \'小米 红米6 Pro 全网通版 4GB内存 曜石黑 32GB 移动联通电信4G手机 双卡双待\', \'价格\': \'¥899.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'33万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t20212/346/2286611589/200381/b60dd7b9/5b4ee328Ne725d6fc.jpg\'} 29 {\'商品名\': \'荣耀畅玩7C 全面屏手机 全网通标配版 3GB+32GB 铂光金 移动联通电信4G手机 双卡双待\', \'价格\': \'¥799.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'72万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t20305/259/1209609364/193755/a3940552/5b21ce25N131ce626.jpg\'} 30 {\'商品名\': \'荣耀Play 全网通版 6GB+64GB 幻夜黑 移动联通电信4G全面屏游戏手机 双卡双待\', \'价格\': \'¥1998.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'33万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t20638/302/805235103/272149/fdafea5c/5b17a2ceN24d043fc.jpg\'} 31 {\'商品名\': \'vivo Z3 6GB+64GB 极光蓝 性能实力派 全面屏游戏手机 移动联通电信全网通4G手机\', \'价格\': \'¥1898.00\', \'店铺\': \'vivo京东自营官方旗舰店\', \'评论量\': \'7.6万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t27616/251/1425719819/224805/20c2401e/5bc831fdN61f8d9d2.jpg\'} 32 {\'商品名\': \'vivo NEX星迹版 零界全面屏AI双摄游戏手机 8GB+128GB 移动联通电信全网通4G手机\', \'价格\': \'¥4298.00\', \'店铺\': \'vivo京东自营官方旗舰店\', \'评论量\': \'9.8万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t1/5586/27/9252/503760/5bac4416E5fd89262/6ea2b3b3dda7e473.jpg\'} 33 {\'商品名\': \'360手机 N7 Pro 全网通 6GB+64GB 玛瑙黑 移动联通电信4G手机 双卡双待 全面屏 游戏手机\', \'价格\': \'¥1699.00\', \'店铺\': \'360手机京东自营旗舰店\', \'评论量\': \'6.8万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t24649/320/757379753/205734/6b7afbaf/5b7baabaNa301c5b2.jpg\'} 34 {\'商品名\': \'锤子(smartisan ) 坚果 Pro 2S 6G+64GB 炫光蓝 全面屏双摄 全网通4G手机 双卡双待 游戏手机\', \'价格\': \'¥1798.00\', \'店铺\': \'锤子科技京东自营旗舰店\', \'评论量\': \'9.9万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t23359/128/2369309375/207652/6973579a/5b7d05feN6f190d9a.jpg\'} 35 {\'商品名\': \'小米MIX2S 全面屏游戏手机 6GB+128GB 黑色 全网通4G 陶瓷手机\', \'价格\': \'¥2999.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'17万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t19702/54/1073670661/187306/da89b445/5ab9e7a8Nbc37a260.jpg\'} 36 {\'商品名\': \'360手机 N7 Lite 全网通 4GB+32GB 幻影黑 移动联通电信4G手机 双卡双待 全面屏 游戏手机\', \'价格\': \'¥999.00\', \'店铺\': \'360手机京东自营旗舰店\', \'评论量\': \'4.9万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t22558/89/2343666456/198377/2e0dea9e/5b7b8cd1Nb4f33692.jpg\'} 37 {\'商品名\': \'vivo X23 8GB+128GB 幻夜蓝 水滴屏全面屏 游戏手机 移动联通电信全网通4G手机\', \'价格\': \'¥3498.00\', \'店铺\': \'vivo京东自营官方旗舰店\', \'评论量\': \'5.9万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t1/4612/28/6223/298257/5ba22d66Ef665222f/d97ed0b25cbe8c6e.jpg\'} 38 {\'商品名\': \'Apple iPhone XS (A2100) 64GB 金色 移动联通电信4G手机\', \'价格\': \'¥8699.00\', \'店铺\': \'京东Apple产品专营店\', \'评论量\': \'8.4万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t1/1468/11/3377/138213/5b997bf3Eda5b24a4/0ace3ed19582dbe6.jpg\'} 39 {\'商品名\': \'小米6X 全网通 4GB+64GB 樱花粉 移动联通电信4G手机 双卡双待 智能手机 拍照手机\', \'价格\': \'¥1299.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'62万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t21160/52/104309289/211615/f4e9fd04/5afbe088Na39708f8.jpg\'} 40 {\'商品名\': \'一加手机6T 8GB+128GB 墨岩黑 光感屏幕指纹 全面屏双摄游戏手机 全网通4G 双卡双待\', \'价格\': \'¥3599.00\', \'店铺\': \'一加手机京东自营官方旗舰店\', \'评论量\': \'4.1万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t1/6425/40/3887/217009/5bd716e9E4886d5d8/b3da975f4047ded3.jpg\'} 41 {\'商品名\': \'三星 Galaxy S 轻奢版(SM-G8750)4GB+64GB 谜夜黑 移动联通电信4G手机 双卡双待\', \'价格\': \'¥2699.00\', \'店铺\': \'三星手机京东自营官方旗舰店\', \'评论量\': \'5万+\', \'图片\': \'https:////img12.360buyimg.com/n7/jfs/t17521/206/2580110270/53998/1e541b83/5afcf165N08093d1a.jpg\'} 42 {\'商品名\': \'荣耀畅玩8C 全网通标配版 4GB+32GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待\', \'价格\': \'¥998.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'3.8万+\', \'图片\': \'https:////img12.360buyimg.com/n7/jfs/t25696/183/1719981196/90401/bcf6106c/5bbac3c5N8b0bd22b.jpg\'} 43 {\'商品名\': \'荣耀8X Max 7.12英寸90%屏占比珍珠屏 4GB+64GB 魅海蓝 移动联通电信4G全面屏手机 双卡双待\', \'价格\': \'¥1499.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'11万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t25582/259/1942499054/80811/1fd3432/5bc06426Nc4199ba0.jpg\'} 44 {\'商品名\': \'联想Z5 6GB+64GB 舒曼黑 6.2英寸全面屏双摄 全网通4G手机 双卡双待\', \'价格\': \'¥1148.00\', \'店铺\': \'联想手机京东自营官方旗舰店\', \'评论量\': \'11万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t22615/93/2155277743/153005/ef373600/5b7673bfN6630f313.jpg\'} 45 {\'商品名\': \'小米8屏幕指纹版 6GB+128GB 黑色 全网通4G 双卡双待 全面屏拍照游戏智能手机\', \'价格\': \'¥3199.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'3万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t1/752/27/6437/203899/5ba1f905E7e8bd0e4/f65e4f8f6c052a59.jpg\'} 46 {\'商品名\': \'荣耀 畅玩7 2GB+16GB 金色 全网通4G手机 双卡双待\', \'价格\': \'¥599.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'26万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t17665/190/2499640910/209789/1b439bbd/5afc0ae1N4f34d0fc.jpg\'} 47 {\'商品名\': \'荣耀畅玩7A 全面屏手机 全网通标配版 2GB+32GB 幻夜黑 移动联通电信4G手机 双卡双待\', \'价格\': \'¥699.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'38万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t18571/219/1315798105/103278/35ed5906/5ac1f1c7N48f753db.jpg\'} 48 {\'商品名\': \'小米Max2 大屏手机 4GB+64GB 金色 全网通4G手机 双卡双待\', \'价格\': \'¥999.00\', \'店铺\': \'小米京东自营旗舰店\', \'评论量\': \'37万+\', \'图片\': \'https:////img12.360buyimg.com/n7/jfs/t6010/111/3843138696/73795/bf58700d/5959ab7fN154e56b4.jpg\'} 49 {\'商品名\': \'华为 HUAWEI Mate 20 麒麟980AI智能芯片全面屏超微距影像超大广角徕卡三摄6GB+128GB亮黑色全网通版双4G手机\', \'价格\': \'¥4499.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'5万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t25954/134/1930444050/488286/31587d0d/5bbf1fc9N3ced3749.jpg\'} 50 {\'商品名\': \'黑鲨游戏手机 Helo 双液冷 更能打 8GB+128GB 极夜黑 全网通4G 双卡双待\', \'价格\': \'¥3499.00\', \'店铺\': \'黑鲨京东自营官方旗舰店\', \'评论量\': \'1.3万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t1/9845/16/1608/68761/5bcefe22Eded9b3bd/dac0b00509961827.jpg\'} 51 {\'商品名\': \'华为 HUAWEI 畅享8e 青春版 2GB+32GB全面屏 金色 全网通版 移动联通电信4G手机 双卡双待\', \'价格\': \'¥699.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'12万+\', \'图片\': \'https:////img12.360buyimg.com/n7/jfs/t21043/186/220467895/46630/3417464c/5b0517ccN295c6fdb.jpg\'} 52 {\'商品名\': \'华为 HUAWEI 畅享9 Plus 4GB+64GB 幻夜黑 全网通 四摄超清全面屏大电池 移动联通电信4G手机 双卡双待\', \'价格\': \'¥1499.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'3.7万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t26038/101/1750775983/176935/5976cd0b/5bbc6e6fN5216f959.jpg\'} 53 {\'商品名\': \'华为 HUAWEI nova 2S 全面屏四摄 4GB +64GB 曜石黑 移动联通电信4G手机 双卡双待\', \'价格\': \'¥1599.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'25万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t22390/274/1879002378/94137/b538aed3/5b3c2de1N926e8968.jpg\'} 54 {\'商品名\': \'华为 麦芒5 全网通 4GB+64GB版 香槟金 移动联通电信4G手机 双卡双待\', \'价格\': \'¥1099.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'20万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t2905/17/3311216817/318397/4b6e0286/578738d1N00bcde3e.jpg\'} 55 {\'商品名\': \'华为 HUAWEI Mate 10 Pro 全面屏徕卡双摄游戏手机 6GB+128GB 银钻灰 全网通移动联通电信4G手机 双卡双待\', \'价格\': \'¥3999.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'23万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t11986/295/1484411523/155164/77795126/5a01503cN19d7f1a0.jpg\'} 56 {\'商品名\': \'三星 Galaxy S8+(SM-G9550)6GB+128GB 谜夜黑 移动联通电信4G手机 双卡双待\', \'价格\': \'¥4299.00\', \'店铺\': \'三星手机京东自营官方旗舰店\', \'评论量\': \'5.7万+\', \'图片\': \'https:////img13.360buyimg.com/n7/jfs/t4894/345/2426573341/236389/f4e04f62/59000294Ncfd9f9ee.jpg\'} 57 {\'商品名\': \'Apple iPhone 6s Plus (A1699) 32G 玫瑰金色 移动联通电信4G手机\', \'价格\': \'¥2799.00\', \'店铺\': \'京东Apple产品专营店\', \'评论量\': \'77万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t3175/89/2690068639/114728/22c2edee/57e4a35bN230918c0.jpg\'} 58 {\'商品名\': \'诺基亚 NOKIA X7 6GB+64GB 沁夜黑 蔡司认证 AI智能美拍 全网通 4G双卡双待 游戏手机\', \'价格\': \'¥1899.00\', \'店铺\': \'诺基亚手机京东自营旗舰店\', \'评论量\': \'2.2万+\', \'图片\': \'https:////img11.360buyimg.com/n7/jfs/t27517/232/1229283307/205990/1347b3c6/5bc43c4dNedd3bd21.jpg\'} 59 {\'商品名\': \'华为 HUAWEI Mate 20 X 麒麟980芯片全面屏超微距影像超大广角徕卡三摄6GB+128GB宝石蓝全网通版双4G手机\', \'价格\': \'¥4999.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'1.5万+\', \'图片\': \'https:////img14.360buyimg.com/n7/jfs/t25792/215/1911566869/331199/4e1aa140/5bbf1d23N3a4d87c5.jpg\'} 60 {\'商品名\': \'荣耀9青春版 全网通 高配版 4GB+32GB 魅海蓝 移动联通电信4G全面屏手机 双卡双待\', \'价格\': \'¥1399.00\', \'店铺\': \'荣耀京东自营旗舰店\', \'评论量\': \'123万+\', \'图片\': \'https:////img10.360buyimg.com/n7/jfs/t15133/132/1070719621/353795/da2168a0/5a43426dNd3854c84.jpg\'} 61 {\'商品名\': \'华为 HUAWEI nova 3e 全面屏2400万前置摄像 4GB +64GB 克莱因蓝 全网通版移动联通电信4G手机 双卡双待\', \'价格\': \'¥1599.00\', \'店铺\': \'华为京东自营官方旗舰店\', \'评论量\': \'28万+\', \'图片\': \'https:////img12.360buyimg.com/n7/jfs/t17422/106/813148544/280435/9521ea80/5aaa6eb4N536936cb.jpg\'} 62 爬取完毕!
到这里可能会有人发现,前三个手机的图片信息显示的done,还有的人运行程序结果是,输出前三条之后就会报错。不要着急,下面就会来解释并处理这个问题。
7、图片懒加载
什么是图片的懒加载?
当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次),当图片出现在浏览器的可视区域内时,才设置图片真正的路径,让图片显示出来,这就是图片懒加载。
简单来说,就是将页面里所有img属性src属性用data-xx代替,当页面滚动直至此图片出现在可视区域时,用js取到该图片的data-xx的值赋给src。
再来看看京东的网页,如图十。
图十
如图十中img标签所示,scr是图片的原地址,而前面的data-lazy-img这个属性就起到了图片懒加载的作用。
回到上文所述,前三张图的结果之所以是显示为“done”,是因为笔者这里把上文,如图十一中,image = good.find(\'div\', class_="p-img").a.img.get(\'src\')中的‘src’改为了‘data-lazy-img’。当页面刚打开时,位于浏览器第一排的商品处于可视区域,所以图片保存的地址位于src中,由于滑轮是瞬间直接拉倒页面底部,所以剩下的图片并没有进入可视区域当中,这时图片懒加载起到了作用,把src中的图片原地址传入了data-lazy-img当中,所以获得data-lazy-img属性时,可以得到剩下所有图片正确的地址信息。
图十一
在了解了问题的原因之后,解决的办法就比较简单了。只需要让模拟浏览器滑动滑轮的这个举动更加“真实”一些,就像人浏览网页,拖动滑轮慢慢滑动,是每个商品都进入可视区域,然后保证每个商品的信息都加在完毕,这样问题就可以解决了。所以只需要将上述滑动滑轮的代码和延时等待的代码修改一下就能实现了。代码实现如下:
1 for y in range(100): 2 js = \'window.scrollBy(0,100)\' 3 browser.execute_script(js) 4 time.sleep(0.1)
8、保存数据
最后需要要把爬取到数据保存至MongoDB就完成了。代码实现如下:
1 MONGO_URL = \'localhost\' 2 MONGO_DB = \'jingdong\' 3 MONGO_collection = \'手机\' 4 client = pymongo.MongoClient(MONGO_URL) 5 db = client[MONGO_DB] 6 7 8 def save_to_mongoDB(result): 9 try: 10 if db[MONGO_collection].insert_one(result): 11 print(\'保存成功!\', result) 12 except Exception: 13 print(\'保存失败!\', result)
保存成功,结果如图十二。
图十二
9、Chrome Headless模式
从Chrome 59版本开始,已经开始支持Headless模式,也就是无界面模式,这样爬取的时候就不会弹出浏览器了。如果要使用此模式,请把Chrome升级到59版本及以上。启用Headless模式的方式如下:
1 chrome_options = webdriver.ChromeOptions() 2 chrome_options.add_argument(\'--headless\') 3 browser = webdriver.Chrome(chrome_options=chrome_options)
当然是用的浏览器还可以对接Firefox,要对接Firefox浏览器,非常简单,只需要更改一处即可:
这里更改了browser对象的创建方式,这样爬取的时候就会使用Firefox浏览器了。
如果不想使用Chrome的Headless模式,还可以使用PhantomJS(它是一个无界面浏览器)来抓取。抓取时,同样不会弹出窗口,还是只需要将WebDriver的声明修改一下即可:
1 browser = webdriver.PhantomJS()
另外,它还支持命令行配置。比如,可以设置缓存和禁用图片加载的功能,进一步提高爬取效率:
1 SERVICE_ARGS = [\'--load-images=false\', \'--disk-cache=true\'] 2 browser = webdriver.PhantomJS(service_args=SERVICE_ARGS)
10、完整代码
下面附上完整代码:
1 from selenium.webdriver.common.by import By 2 from selenium.webdriver.support import expected_conditions as EC 3 from selenium.webdriver.support.wait import WebDriverWait 4 from selenium import webdriver 5 from bs4 import BeautifulSoup 6 from urllib import parse 7 import time 8 from jd_shouji_config import * # 该模块的内容:MONGO_URL = \'localhost\' MONGO_DB = \'jingdong\' MONGO_collection = \'products\' 9 import pymongo 10 11 # 启动chrome headless模式,即无界面模式,这样爬取的过程中就不会弹出浏览器了 12 chrome_options = webdriver.ChromeOptions() 13 chrome_options.add_argument(\'--headless\') 14 browser = webdriver.Chrome(chrome_options=chrome_options) 15 wait = WebDriverWait(browser, 10) 16 client = pymongo.MongoClient(MONGO_URL) 17 db = client[MONGO_DB] 18 19 20 def get_url(n, word): 21 print(\'正在爬取第\' + str(n) + \'页\') 22 # 确定搜索商品的内容 23 keyword = {\'keyword\': word} 24 # 页面n与参数page的关系 25 page = \'&page=%s\' % (2 * n - 1) 26 # 使用urlencode将keyword转码成可识别的url格式,enc以utf-8方式编码,并得传入对应page,得到完整的url 27 url = \'https://search.jd.com/Search?\' + parse.urlencode(keyword) + \'&enc=utf-8\' + page 28 return url 29 30 31 def parse_page(url): 32 print(\'爬取信息并保存中...\') 33 browser.get(url) 34 # 把滑轮慢慢下拉至底部,触发ajax 35 for y in range(100): 36 js = \'window.scrollBy(0,100)\' 37 browser.execute_script(js) 38 time.sleep(0.1) 39 wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, \'#J_goodsList .gl-item\'))) 40 html = browser.page_source 41 soup = BeautifulSoup(html, \'lxml\') 42 # 找到所有商品标签 43 goods = soup.find_all(\'li\', class_="gl-item") 44 # 遍历每个商品,得到每个商品的信息 45 for good in goods: 46 tag = good.find(\'div\', class_="p-price").strong.em.string 47 money = good.find(\'div\', class_="p-price").strong.i.string 48 # 这里有一个bug!就是京东有些商品竟然没有店铺名,导检索store时找不到对应的节点导致报错 49 store = good.find(\'div\', class_="p-shop").span 50 commit = good.find(\'div\', class_="p-commit").strong.a.string 51 name = good.find(\'div\', class_="p-name p-name-type-2").a.em 52 image = good.find(\'div\', class_="p-img").a.img.get(\'src\') # 图片懒加载 data-lazy-img 53 if store is not None: 54 new_store = store.a.string 55 else: 56 new_store = \'没有找到店铺 - -!\' 57 new_name = \'\' 58 for item in name.strings: # 由于name.strings生成的是一个包含多个string的生成器,所以需要遍历出所有的string,再通过字符串的拼接成完整的新字符串 59 new_name = new_name + item 60 product = { 61 \'商品名\': new_name, 62 \'价格\': tag + money, 63 \'店铺\': new_store, 64 \'评论量\': commit, 65 \'图片\': \'https://\' + image 66 } 67 save_to_mongoDB(product) 68 69 70 def save_to_mongoDB(result): 71 try: 72 if db[MONGO_collection].insert_one(result): 73 print(\'保存成功!\', result) 74 except Exception: 75 print(\'保存失败!\', result) 76 77 78 def main(): 79 try: 80 word = input(\'请输出你想要爬取的商品:\') 81 pages = int(input(\'请输入你想要抓取的页数(范围是1-100):\')) 82 # 京东最大页面数为100 83 if 1 <= pages <= 100: 84 page = pages + 1 85 for n in range(1, page): 86 url = get_url(n, word) 87 parse_page(url) 88 print(\'爬取完毕!\') 89 browser.close() 90 else: 91 print(\'请重新输入!\') 92 main() 93 except Exception as error: 94 print(\'出现异常!\', error) 95 return None 96 97 98 if __name__ == \'__main__\': 99 main()
结语:
通过本次爬虫项目的编写,感觉受益良多,也感觉到写好一篇博客和写好一个程序一样不简单,希望以后能继续坚持写,并且能越写越好,最后有不足之处希望能谅解并且指出。