爬虫连载系列(3)--用Selenium+xpath爬取京东商城

前言

这两天原本想在淘宝上爬点东西进行分析的,但没想到淘宝的反爬机制对我这个爬虫菜鸡充满了恶意。先是被数据的格式搞得焦头烂额,好不容易写好了测试一页的代码,准备美滋滋开始大显身手,爬取多页时,发现竟然被封IP了!呜呜┭┮﹏┭┮。于是,开始研究各种反反爬的机制,IP代理,多线程、模拟登陆... ...发现自己的盲区越来越大。眼瞅着与自己的博客更新计划越行越远,只好先换个目标,对某东下手。但并不代表我会放过它,等自己在修炼一段时间,再来会会它。下面,我们开始进入正题吧。

这次想做一个关于糖果的分析,于是爬取了京东共2700左右条的数据,这个数据应该是够自己分析了。京东比较坑的一点是,他的每一页是先加载一部分,另一部分是通过动态加载的。为此,我的解决办法是,使用Selenium构建模拟浏览器,先通过执行脚本,下滑到底部,等全部数据加载完成后,在进行读取。哦,对了,这次使用的解析库是xpath。

要点

用到的库:selenium、urllib、pandas、csv、lxml

工具: python 3.7、jupyter notebook

分析

网页分析

首先,我们进入京东商城,搜索框中输入糖果,进入开发者模式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ySX78DDF-1584365178843)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20200316202344764.png)]

发现这也太简单了叭,按捺不住内心的鸡冻,马上就想要开始写代码。

慢着

img

我们先看看Network的输出,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nZSpnium-1584365178851)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20200316203358532.png)]

细细看一下Preview中的商品,如果你点一下,你会发现只有30个商品,而页面中共有60个商品,你会发现事情并不简单。

下滑到底部后,你在刷新下Network输出,你会发现,这里多了个这个js文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OIU06gjc-1584365178852)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20200316203704329.png)]

而这里就是剩下的30条数据。

哦,我懂了,不就是先加载30条数据,后30条数据在你滑动浏览的过程中加载嘛。

img

这时候,我们前面介绍的主角就登场了:Selenium 。通过它,构建一个虚拟浏览器,先执行一个脚本,下滑到最底部,加载完数据后我们在进行爬取。

注意:使用他,你要先安装谷歌插件(谷歌浏览器的话),还得添加驱动。具体操作步骤,请移步百度。

url分析

分析完网页结构后,我们来分析下url的构成

前30条数据的

https://search.jd.com/Search?keyword=%E7%B3%96%E6%9E%9C&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&page=1&s=1&click=0

后30条数据的

https://search.jd.com/s_new.php?keyword=%E7%B3%96%E6%9E%9C&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%E7%B3%96%E6%9E%9C&page=2&s=31&scrolling=y&log_id=1584362139.53979&tpl=1_M&show_items=100007265728,4695858,1227178,4838836,326467,100000424178,100000217809,1083784,4005567,1153605,1153613,4707850,1153610,1178879,4476739,794422,100008420904,100000757536,6338667,100004891346,4476767,30494640062,4491861,3816695,523631,4476753,2210373,679518,3692931,903403

发现两个中,前面构成是基本一致的,

keyword:搜索关键字

log_id:是后30个商品的商品id

经过实验,我们发现简化后可用的url:https://search.jd.com/Search?keyword=%E7%B3%96%E6%9E%9C&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&page=1&s=1&click=0

在看下第二页:

https://search.jd.com/Search?keyword=%E7%B3%96%E6%9E%9C&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&page=3&s=61&click=0

第三页 (此处省略100字) ... ...

得出url的规律: page = 2*页数 - 1

分析完这些后,就可以进行快乐的编码模式了

代码

直接上代码吧

import time
from selenium import webdriver 
from  lxml import etree
import urllib
import csv
import pandas as pd 
import random

#获取页面
def get_page(depth):
    keyword = "糖果"
    headers = {
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36"

    }
    base_url = 'https://search.jd.com/Search?keyword={}&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&stock=1&page={}'
    for page_num in range(1,depth):
        try:
            start_url = base_url.format(urllib.parse.quote(keyword),page_num*2-1)
            driver = webdriver.Chrome()
            driver.get(start_url) 
            driver.execute_script("window.scrollTo(0,document.body.scrollHeight);")  #执行下滑到底部的操作
            time.sleep(10) #必须休眠,等待获取完全部信息
            #获取页面信息
            source = driver.page_source  #  等同于  response = requests.get(url = start_url,headers=headers)
            html = etree.HTML(source)
            item = parse_page(html)
            write_excel(item)
            print('爬取第'+str(page_num)+'页时成功!')
            time.sleep(random.randint(2,6))
        except:
            print('爬取第'+str(page_num)+'页时出错!')
            continue
   
#解析页面
def parse_page(html):
    li = html.xpath('//*[@id="J_goodsList"]/ul/li')
    for one_li in li: 
        yield{
            'price':one_li.xpath('div/div[2]/strong/i/text()')[0],
            'title':get_title(one_li),
            'comment_num':one_li.xpath('div/div[4]/strong/a/text()')[0],
            'shop' :get_shop(one_li),
            'goods_url':'http://'+one_li.xpath('div/div[1]/a/@href')[0]        
        }

# #获取标题
def get_title(item):
    title_list = item.xpath('div/div[3]/a/em/text()')
    title = ' '.join(title_list)
    return title 
    
    
#获取店铺名称
def get_shop(item):
    shop=item.xpath('div/div[5]/span/a/text()')
    if len(shop) == 0:
        return '未知'
    else:
        return shop[0]

#写入csv文件中
def write_excel(item):
    good_df = pd.DataFrame(item)
    good_df.to_csv('./JongDong.csv',mode='a',encoding= 'utf-8-sig')
    

def main():
    get_page(50)
    
if __name__ == "__main__":
    main()

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1A2JDVeQ-1584365178862)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20200316210020249.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9zvOfeAK-1584365178865)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20200316210255995.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgj7bs6j-1584365178869)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20200316210637929.png)]

嗯,很好,都爬取成功了

img

真的嘛?你不是说一页60,60*49 = 2940.你别欺负我读书少!!!

好吧,我承认是漏了些数据。但我发4,这绝对不是我代码的问题,我测试过爬取单页和爬前几页时是能够爬取完整的。真的只是我家网的问题,没来得及加载完全部数据。虽然我也设置了延时(休眠10s来获取数据),但可能这些时间里刚好碰上网卡情况。下午爬取时候,确实有段时间网特慢。下次,考虑加入retry,让它没爬取完整就重新爬,直到一页的数据爬取完整,变成一个真正合格的爬虫,同时,加入多线程,减少爬取的时间。再说,两千多条也足够用了,不是嘛。嘻嘻(●'◡'●)

后记

后面将用今天获得的数据进行分析,解锁糖果的密码。(●'◡'●)

猜你喜欢

转载自www.cnblogs.com/feixiaofei/p/12507001.html