Python爬虫-selenium

2020-10-09 14:56:43 浏览数 (1)

有态度地学习

对于Ajax加载的网页已经分析了好几回,这回来说说利用selenium自动化获取网页信息。

通常对于异步加载的网页,我们需要查找网页的真正请求,并且去构造请求参数,最后才能得到真正的请求网址。而利用selenium通过模拟浏览器操作,则无需去考虑那么多,做到可见即可爬。

当然带来便捷的同时,也有着不利,比如说时间上会有所增加,效率降低。可是对于业余爬虫而言,更快的爬取,并不是那么的重要。

首先在电脑的PyCharm上安装selenium,然后下载与电脑上谷歌浏览器相对应版本的ChromeDriver。由于我的Mac系统版本较新,需要先关闭Rootless内核保护机制,才能够安装,所以也是折腾一番后才成功安装。

针对京东商城笔记本的网页进行分析,这回只要在网页源码上分析,就可以获取笔记本价格、标题、评论数、商家名称、商家性质。

爬取代码如下:

代码语言:javascript复制
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium import webdriver
from bs4 import BeautifulSoup
import pymongo
import time

# 连接数据库
client = pymongo.MongoClient(host='localhost', port=27017)
db = client.JD_products
collection = db.products

# 启动浏览器
browser = webdriver.Chrome()
wait = WebDriverWait(browser, 50)


def to_mongodb(data):
    # 存储数据信息
    try:
        collection.insert(data)
        print("Insert The Data Successfully")
    except:
        print('Insert The Data Failed')


def search():
    browser.get('https://www.jd.com/')
    try:
        # 查找搜索框及搜索按钮,输入信息并点击按钮
        input = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "#key")))
        submit = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#search > div > div.form > button")))
        input[0].send_keys('笔记本')
        submit.click()
        # 查找笔记本按钮及销量按钮,依次点击按钮
        button_1 = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#J_selector > div:nth-child(2) > div > div.sl-value > div.sl-v-list > ul > li:nth-child(1) > a")))
        button_1.click()
        button_2 = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#J_filter > div.f-line.top > div.f-sort > a:nth-child(2)")))
        button_2.click()
        # 获取总页数
        page = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > em:nth-child(1) > b')))
        return page[0].text
    except TimeoutException:
        search()


def next_page(page_number):
    try:
        # 滑动到网页底部,加载出所有商品信息
        browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(10)
        html = browser.page_source
        parse_html(html)
        # 当网页到达100页时,下一页按钮失效,所以选择结束程序
        while page_number == 101:
            exit()
        # 查找下一页按钮,并点击按钮
        button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.pn-next > em')))
        button.click()
        wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "#J_goodsList > ul > li:nth-child(60)")))
        # 判断翻页成功
        wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, "#J_bottomPage > span.p-num > a.curr"), str(page_number)))
    except TimeoutException:
        return next_page(page_number)


def parse_html(html):
    """
    解析商品列表网页
    """
    data = {}
    soup = BeautifulSoup(html, 'html.parser')
    goods_info = soup.select('.gl-item')
    # 查看当前页商品数量,了解是否还有未加载的商品
    quantity = 'item: '   str(len(goods_info))
    print(quantity)
    for info in goods_info:
        # 获取商品标题信息
        title = info.select('.p-name.p-name-type-2 a em')[0].text.strip()
        title = title.replace('爱心东东', '')
        print("title: ", title)
        data['_id'] = title
        # 获取商品价格信息
        price = info.select('.p-price i')[0].text.strip()
        price = int(float(price))
        print("price: ", price)
        data['price'] = price
        # 获取商品的评论数量
        commit = info.select('.p-commit strong')[0].text.strip()
        commit = commit.replace('条评价', '')
        if '万' in commit:
            commit = commit.split("万")
            commit = int(float(commit[0]) * 10000)
        else:
            commit = int(float(commit.replace(' ', '')))
        print("commit: ", commit)
        data['commit'] = commit
        # 获取商品的商店名称
        shop_name = info.select('.p-shop a')
        if (len(shop_name)) == 1:
            print("shop_name: ", shop_name[0].text.strip())
            data['shop_name'] = shop_name[0].text.strip()
        else:
            print("shop_name: ", '京东')
            data['shop_name'] = '京东'
        # 获取商品的商店属性
        shop_property = info.select('.p-icons i')
        if (len(shop_property)) >= 1:
            message = shop_property[0].text.strip()
            if message == '自营':
                print("shop_property: ", message)
                data['shop_property'] = message
            else:
                print("shop_property: ", '非自营')
                data['shop_property'] = '非自营'
        else:
            print("shop_property: ", '非自营')
            data['shop_property'] = '非自营'
        to_mongodb(data)
        print(data)
        print("nn")


def main():
    total = int(search())
    print(total)
    for i in range(2, total 2):
        time.sleep(20)
        print("第", i-1, "页:")
        next_page(i)


if __name__ == "__main__":
    main()

虽然一开始就是以笔记本这个关键词去搜索,但是这里还是需要再点击一次笔记本按钮,这是因为直接搜索笔记本会出现平常上课做笔记的那种笔记本,导致会获取无用信息。所以利用京东自身更加详细的归类,得到我们想要的信息。

其中每一个网页有60条商品数据,那么按道理应该有6000条的笔记本商品信息,但是最后却只获取了5992条。

估计两个原因:

1⃣️在MongoDB中商品的标题为主键,商品标题出现重复

2⃣️网页未能加载完所有的商品信息

最后成功获取商品信息

读取MongoDB中数据进行可视化分析

代码语言:javascript复制
from pyecharts import Bar
import pandas as pd
import numpy as np
import pymongo

client = pymongo.MongoClient('localhost', 27017)
db = client.JD_products
table = db.products
df = pd.DataFrame(list(table.find()))
shop_message = df[df.shop_property == '自营'].groupby(['shop_name'])
shop_com = shop_message['shop_name'].agg(['count'])
shop_com.reset_index(inplace=True)
shop_com_last = shop_com.sort_values('count', ascending=False)[:12]
attr = np.array(shop_com_last['shop_name'])
v1 = np.array(shop_com_last['count'])
attr = ["{}".format(i.replace('京东', '').replace('旗舰店', '').replace('自营', '').replace('官方', '').replace('京东', '').replace('电脑', '').replace('产品专营店', '').replace('工作站', '').replace('笔记本', '')) for i in attr]
v1 = ["{}".format(i) for i in v1]
bar = Bar("京东自营商店笔记本种类排行", title_pos='center', title_top='18', width=800, height=400)
bar.add("商家", attr, v1, is_convert=True, xaxis_min=10, yaxis_label_textsize=12, is_yaxis_boundarygap=True, yaxis_interval=0, is_label_show=True, is_legend_show=False, label_pos='right', is_yaxis_inverse=True, is_splitline_show=False)
bar.render("京东自营商店笔记本种类排行.html")

从上面可以看出,ThinkPad位居榜首,也与后面的词云图有所呼应。商务、办公,因为它就是一款以商务办公为主打品牌的笔记本。此外国内品牌联想、华硕、宏碁、华为也在榜上,支持国货!!!

代码语言:javascript复制
from pyecharts import Bar
import pandas as pd
import pymongo

client = pymongo.MongoClient('localhost', 27017)
db = client.JD_products
table = db.products
df = pd.DataFrame(list(table.find()))
price_info = df['price']
bins = [0, 2000, 2500, 3000, 3500, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, 16000, 19000, 200000]
level = ['0-2000', '2000-2500', '2500-3000', '3000-3500', '3500-4000', '4000-5000', '5000-6000', '6000-7000', '7000-8000', '8000-9000', '9000-10000', '10000-12000', '12000-14000', '14000-16000', '16000-19000', '19000以上']
price_stage = pd.cut(price_info, bins=bins, labels=level).value_counts().sort_index()
attr = price_stage.index
v1 = price_stage.values
bar = Bar('笔记本价格分布柱状图',  title_pos='center', title_top='10', width=800, height=400)
bar.add('', attr, v1, is_stack=True, xaxis_rotate=30, yaxis_min=0, xaxis_interval=0, is_splitline_show=False, is_label_show=True)
bar.render('笔记本价格分布柱状图.html')

笔记本价格区间在4000-6000有较大的集中,也一定程度反应出了现在笔记本的中间价位,记得刚上大学那会,价格在5000 的笔记本就能有着不错的配置,LOL特效全开。

代码语言:javascript复制
from pyecharts import Pie
import pandas as pd
import pymongo

client = pymongo.MongoClient('localhost', 27017)
db = client.JD_products
table = db.products
df = pd.DataFrame(list(table.find()))
shop_message = df.groupby(['shop_property'])
shop_com = shop_message['shop_property'].agg(['count'])
shop_com.reset_index(inplace=True)
shop_com_last = shop_com.sort_values('count', ascending=False)
attr = shop_com_last['shop_property']
v1 = shop_com_last['count']
pie = Pie('商店性质', title_pos='center', width=800, height=400)
pie.add('', attr, v1, radius=[40, 75], label_text_color=None, is_label_show=True, legend_orient='vertical', legend_pos='left')
pie.render('商店性质.html')

统计下来自营与非自营,还是小巫见大巫。京东和淘宝最大的区别就是京东有自营产品,送货也快。虽说自营的也有假货,但是还是小概率事件。购买电子产品时,比如手机、电脑等,对于我这种小白而言,我第一选择就是去官网或者京东自营店购买,我是绝对不会去电子城和奸商们斗智斗勇的,即使可能价格会低点。但是官网一般快递比较慢,需要个3-5天,而京东可能只需1-2天,所以京东算是我购买的最优选择。

代码语言:javascript复制
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
import matplotlib.pyplot as plt
import pandas as pd
import pymongo
import jieba
import re

client = pymongo.MongoClient('localhost', 27017)
db = client.JD_products
table = db.products
data = pd.DataFrame(list(table.find()))
data = data[['_id']]

text = ''
for line in data['_id']:
    r = '[a-zA-Z0-9’!"#$%&'()* ,-./:;<=>?@,。?★、…【】《》?“”‘’![\]^_`{|}~] '
    line = re.sub(r, '', line.replace('笔记本电脑', '').replace('英寸', ''))
    text  = ' '.join(jieba.cut(line, cut_all=False))
backgroud_Image = plt.imread('computer.jpeg')
wc = WordCloud(
    background_color='white',
    mask=backgroud_Image,
    font_path='msyh.ttf',
    max_words=2000,
    stopwords=STOPWORDS,
    max_font_size=130,
    random_state=30
)
wc.generate_from_text(text)
img_colors = ImageColorGenerator(backgroud_Image)
wc.recolor(color_func=img_colors)
plt.imshow(wc)
plt.axis('off')
wc.to_file("computer.jpg")
print("生成词云成功")

这里把标题中笔记本配置参数全部用正则筛选掉。虽说笔记本参数决定了笔记本的性能,不过真正的去购买一台笔记本时,最重要的还是根据自己的需求和预算,然后再去考虑笔记本参数,最后选择一部适合自己的笔记本。一般的笔记本参数如下:

CPU:酷睿系列i3、i5、i7,标压M与低压U

硬盘:500G、1T、2T

显卡:AMD,NVIDIA

内存:4G,8G

0 人点赞