【Python Onramp】9.Python爬虫(2):selenium爬取京东商品信息和知乎热榜信息

系列文章目录

【Python Onramp】 0. 卷首语

上一篇:【Python Onramp】8.Python爬虫(1)基于requests和BeautifulSoup的全国区划数据爬虫

项目描述

如果使用上一节的requests,将不能实现对于知乎的有效爬取。由于知乎具有较强的反爬机制,所以我们需要换用一套可以用于更高程度模拟用户行为的框架,即selenium。

这个项目中,你将学习到selenium框架的使用,并利用selenium模拟用户行为爬取知乎热榜。
代码仓库https://github.com/Honour-Van/CS50/tree/master/WebSpider2

任务1

用手工登录配合的方法,获取登录知乎的 cookies ,供后续使用 cookies保存在./data/my_cookies.json,后续程序使用时也指向这个位置,以便核查时更换其他cookies。这是一个单独的程序,和后续程序可以各自独立运行

  1. 使用已有的 cookies ,用 selenium 库登录知乎,抓取热榜话题和对应的热度值

基于如上任务,我们做出如下1_get_cookies.py2_hot_track.py两个文件,分别对应两个指定任务。

要点总览

要点1:selenium的使用

我们这里使用的非常简单,可以在具体实现过程当中以练代讲。
这是一个参考https://blog.csdn.net/weixin_41931602/article/details/82754743

要点2:xpath方法筛选元素

在实现过程当中,我们本来想使用id或name等方法直接查找,但经常页面没有 id,name 这些属性值,而class name 重复性较高,link 定位有针对性,所以 Xpath 与 Css 定位更灵活些,见如下两篇博客中所述。

https://www.cnblogs.com/minieye/p/5803640.html

https://blog.csdn.net/qq_32189701/article/details/100176577

具体实现

selenium获取cookies

我们这里以获取cookies的程序来介绍selenium的用法。

我们这里以chrome为例,安装chromedriver的方法如下https://www.cnblogs.com/lfri/p/10542797.html

import json
from selenium import webdriver # selenium的浏览器控制器
from selenium.webdriver.chrome.options import Options #用于设置浏览器启动的一些参数

options = Options()            # 这个选项类似于在Echarts中的Options,都是将相关属性通过键值对的方式组织在一起
#options.add_argument("--headless")  # 不打开浏览器界面,以节省时间
browser = webdriver.Chrome(options=options) # 初始化一个浏览器控件
browser.get('https://www.zhihu.com/')
browser.maximize_window()

input("请用手机扫码登录,然后按回车……")  # 等待用手机扫码登录, 登录后回车即可

cookies_dict = browser.get_cookies()  # 获取当前登陆的cookies信息
cookies_json = json.dumps(cookies_dict) # cookies信息转化为json格式
#print(cookies_json)

# 登录完成后,将cookies保存到本地文件
out_filename = './data/my_cookies.json'
out_file = open(out_filename, 'w', encoding='utf-8')
out_file.write(cookies_json) # cookies信息保存
out_file.close()
print('Cookies文件已写入:' + out_filename)

browser.close()

一个经验是,如果不使用close,那么在notebook中进行selenium爬虫将会非常方便,每一个block都相当于是一个单独的操作行为,而窗口一直打开,也可以使用手动干预。

知乎热榜爬虫

由于实现还是比较困难,课程中的任务是将一个爬取京东信息手动改成知乎爬虫。

京东示例代码如下:

# -*- coding: utf-8 -*-
"""
Created on Sun Apr 26 00:08:48 2020
@author: Justin
"""
'''
在京东网站上搜索商品
保存当前页的商品信息后,翻到指定页码继续保存商品信息
'''

# 打开浏览器
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from pyquery import PyQuery as pq
import time
import random
browser = webdriver.Chrome()
# browser.maximize_window()
wait = WebDriverWait(browser, 10)  # 设置等待页面加载的最长超时时间

# ---函数:搜索关键词--------------------------


def search(keyword):
    inbox = browser.find_element_by_id('keyword')  # 查找搜索输入框(提前检查html源码确认id)
    inbox.send_keys(keyword)
    inbox.send_keys(Keys.ENTER)
    wait.until(EC.presence_of_element_located(
        (By.ID, 'J_bottomPage')), message='in function search')  # 等待页面底部的元素加载完成

# ---函数:翻到下一页--------------------------


def next_page():
    # 下面代码用于控制侧边滚动条,拉到页面底端
    # 该页面很长,不这么操作,未显示在屏幕范围内的页面内容不会被加载
    browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(1)
    # 翻页动作
    nextpage_btn = wait.until(EC.element_to_be_clickable(
        (By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.pn-next > em')))
    nextpage_btn.click()
    print('翻到下一页')

# ---函数:翻到指定页,并等待页面加载完成--------------------------


def index_page(pg_num):
    # 拉到页面底端,让页码输入框等元素得以加载
    browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(1)
    inbox = wait.until(EC.presence_of_element_located(
        (By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > input')),
        message='in function index_page:inbox')
    submit_btn = wait.until(EC.presence_of_element_located(
        (By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > a')),
        message='in function index_page:submit button')
    inbox.clear()
    inbox.send_keys(str(pg_num))
    submit_btn.click()
    print('翻到第{}页'.format(pg_num))


# ---函数:等待指定页面加载完成---------------------
def wait_page_load(pg_num):
    # time.sleep(1) # 若没有合适的元素用于判断页面加载完成,设置固定延时用于等待页面加载
    wait.until(EC.text_to_be_present_in_element(
        (By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.curr'), str(pg_num)),
        message='in function wait_page_load')  # 等待页面底部的当前页码出现
    print('第{}页加载完成'.format(pg_num))

# ---函数:获取商品信息---------------------


def get_goods():
    for i in range(100):  # 慢慢向下滑动窗口,让所有商品信息加载完成
        browser.execute_script('window.scrollTo(0, {});'.format(i*100))
        time.sleep(0.1)

    pq_doc = pq(browser.page_source)

    for i in range(1, 61, 10):  # 每页包含60个商品,仅解析一部分作为测试
        # 这里只是打印部分商品信息,进一步应该保存到文件中
        print('---{}---'.format(i))
        item = pq_doc('#J_goodsList > ul > li:nth-child({})'.format(i))
        print(item.find('.p-img a > img').attr('src'))
        #print(item.find('.p-name.p-name-type-2 > a > em').text())
        print(item.find('.p-price > strong > i').text())

# ----------------------------------------------
# ---主程序-------------------------------------


browser.get('https://search.jd.com')
search('冰箱')  # 搜索产品信息
wait_page_load(1)  # 等待第1页加载完成
get_goods()  # 获取当前页面的产品信息

for i in range(2, 5):  # 翻页并处理
    time.sleep(1+random.random())  # 适当延时1.x秒,规避网站反爬虫机制
    index_page(i)  # 翻到第i页
    wait_page_load(i)  # 等待第i页加载完成
    get_goods()

time.sleep(1)
browser.close()

更改为知乎热榜信息如下,同示例程序不同的是,等待选项需要自己另行构建。如果不等待,直接爬出内容,就会发现我们的

之前我们使用了查找按钮上文字的方法,这里我们观察到,热榜共50条,最下部有一个class为HotList-end的元素。
在这里插入图片描述
我们等待其加载好即可。

wait.until(EC.presence_of_element_located(
    (By.XPATH, '//div[@class="HotList-end"]')), message="wait hotlist loading")  # 等待页面底部的当前页码出现

具体爬取过程中,我们遇到了一些小麻烦,比如top3的class和后续元素的class不一定相同。

[<div class="HotItem-rank HotItem-hot">1</div>]
[<div class="HotItem-rank HotItem-hot">2</div>]
[<div class="HotItem-rank HotItem-hot">3</div>]
[<div class="HotItem-rank">4</div>]
[<div class="HotItem-rank">5</div>]
[<div class="HotItem-rank">6</div>]
[<div class="HotItem-rank">7</div>]
[<div class="HotItem-rank">8</div>]
[<div class="HotItem-rank">9</div>]
[<div class="HotItem-rank">10</div>

我们最先开始使用HotItem-rank HotItem-hot整个作为类名查找,发现甚至不能找出前三个。但是后面的都可以通过 HotItem-rank 作为 class 名筛选出来。

最终代码如下:

import json
import time
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup

options = Options()
# options.add_argument("--headless")
browser = webdriver.Chrome(options=options)


browser.get("https://www.zhihu.com")
browser.maximize_window()


browser.delete_all_cookies()
cookie_filename = "./data/my_cookies.json"
cookies_file = open(cookie_filename, 'r', encoding='utf-8')
cookies = json.load(cookies_file)
for cookie in cookies:
    browser.add_cookie({
        'domain': '.zhihu.com',
        'name': cookie['name'],
        'value': cookie['value'],
        'path': '/',
        'expires': None
    })

browser.get("https://www.zhihu.com")
time.sleep(3)

browser.save_screenshot("./output/zhihu_login.png") # 保存图片

hot_button = browser.find_element_by_xpath("//a[@href='/hot']")
hot_button.click()


wait = WebDriverWait(browser, 10)
for i in range(100):  # 慢慢向下滑动窗口,让所有商品信息加载完成
    browser.execute_script('window.scrollTo(0, {});'.format(i*100))
    time.sleep(0.1)

wait.until(EC.presence_of_element_located(
    (By.XPATH, '//div[@class="HotList-end"]')), message="wait hotlist loading")  # 等待页面底部的当前页码出现
browser.save_screenshot("./output/zhihu_hot.png")

f = open("./output/hot.txt", 'w', encoding='utf-8')
print("知乎热榜:", datetime.now().strftime("%Y-%m-%d %H:%M:%S"), file=f)

soup = BeautifulSoup(browser.page_source, 'html.parser')
for item in soup.select("section.HotItem"):
    item_index = item.select("div.HotItem-rank")
    print(item_index[0].get_text(), file=f)
    item_title = item.select("h2.HotItem-title")
    print("问题:", item_title[0].get_text(), file=f)
    item_excerpt = item.select("p.HotItem-excerpt")
    if len(item_excerpt):
        print("概述:",item_excerpt[0].get_text(), file=f)
    item_metrics = item.select("div.HotItem-metrics")
    print("热度:",item_metrics[0].get_text()[:-7] + "万", file=f)

总结

一个思想:爬虫具有攻防性质。如果网站使用了更好的爬虫机制,就可以有相应的反爬机制,这当然是在一定的线度内的。

几个要点

  • xpath选择元素
  • selenium爬虫框架
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值