使用Selenium模拟浏览器抓取淘宝商品美食信息

需要用到的有

Selenium + Chrome/PhantomJS

PhantomJS : 无界面浏览器

用的解析器: Pyquery

存储用的: MongoDB

分析页面

  1. 打开 taobao.com,并搜索 “美食”
  2. 检查页面,打开 Network 并勾选 preserve log
  3. 选择 preview 选项卡,在代码里面我们看不到任何商品的信息。

整体流程

首先用 Selenium模拟浏览器获取页面,然后选择搜索框并且输入“美食”并且搜索,然后在尾部输入 数字 翻页。

Selenium with Python 等待

http://selenium-python.readthedocs.io/waits.html

MongoDB 安装与配置

不用关心表的结构,直接将字典结构的动态数据直接插入就好了。

安装

brew install mongodb

启动

brew services start mongodb

进入

mongo

可视化管理软件

https://www.mongodb.com/

PhantomJS无界面浏览器安装

安装

brew install phantomjs

配置环境变量

理论上是会直接把环境变量安装到 /usr/local/bin, 但是由于我们用的虚拟环境。 所以需要查看 替身文件 所连接的源文件拷贝到 虚拟环境的文件夹里面的 bin 文件夹内。

20171027150907344569600.png

或者到官网 http://phantomjs.org/download.html 下载文件,再把安装包里面的文件夹bin 连的 文件拷贝到 虚拟环境里面的 bin文件夹内。

配置参数

http://phantomjs.org/api/command-line.html

可以设置例如: 是否加载图片(不加载提高速度) , 使用缓存 等等。

20171027150907363298432.png

完整代码+说明

sipier.py

import re
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from pyquery import PyQuery as pq  # 导入 PyQuery 并重命名为 pq
from config import *   # 引入 config.py 里面的所有变量
import pymongo

client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]   #链接到 MONGODB


browser = webdriver.PhantomJS()    # 调用 PhantomjS 浏览器, 调试阶段用chrome。调试好了就用PhantomjS 无界面浏览器
wait = WebDriverWait(browser, 10)  # 浏览器等待传入 wait

browser.set_window_size(1400, 900)   #设置浏览器窗口大小,有些流量器窗口太小会显示不全

def search():  #定义搜索函数
    print('正在搜索')  #由于没有界面,所以用这个看看状态
    try:
        browser.get('https://www.taobao.com')
        input = wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR, '#q'))
        )   # 拿到淘宝搜索框
        submit = wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_TSearchForm > div.search-button > button')))
        # 拿到搜索按钮
        input.send_keys(KEYWORD)   # 在这里搜索(KEYWORD),keyword 在config.py里面,在搜索框输入"美食"
        submit.click()   # 点击搜索按钮
        total = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.total')))
        # 等待元素(下面的页码显示区块)加载完成
        get_products() #页面完全加载好了后再调用 这个函数。此函数在下面。
        return total.text #获取加载完成的页码区块的字符串
    except TimeoutException:    # 假如上面的操作失败
        return search()   # 重新再执行一次 def search() 这个函数

def next_page(page_number):   # 定义翻页函数(传入页码)
    print('正在翻页', page_number)
    try:
        input = wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > input'))
        ) # 拿到页码输入框
        submit = wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > span.btn.J_Submit')))
        # 拿到页码后面的 确定 按钮
        input.clear() # 清除页码输入框的内容
        input.send_keys(page_number)  #输入页码
        submit.click()  # 点击确定按钮,执行翻页操作
        wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > ul > li.item.active > span'), str(page_number)))
        # 判断上面的翻页操作是否成功,用到函数 —— 在元素中有这样的文字(text_to_be_present_in_element),当翻页成功后,判断当前的高亮页码的文字, str(page_number) 为当前要传入判定的text,用这个页码和 CSS 选择器里面你的 text 比较
        get_products() # 翻页成功后再调用一下。
    except TimeoutException:
        next_page(page_number) # 如果出错就从新执行这个请求

def get_products():  # 这个函数需要在 search() 加载完是用一次,每次 next_page(page_number) 加载完一个新的页面调用一次。
    wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-itemlist .items .item')))
    # 判断加载成功,id 为mainsrp-itemlist 里面的 class items 里面的 item。 如果通过了就表示所有的宝贝信息都已经加载成功了
    html = browser.page_source  # 拿到正在网页的源代码
    doc = pq(html)   # 把网页的源代码传入 doc
    items = doc('#mainsrp-itemlist .items .item').items()  # 后面调用 .items() 可以选着 doc 里面的所有 .item ,每个item是一个宝贝信息
    for item in items:
        product = {
            'image': item.find('.pic .img').attr('src'), #从上面的代码获取图片(获取 class .pic 里面的.img 的属性(attr) src
            'price': item.find('.price').text(), # 从 class .price 里面获取 text
            'deal': item.find('.deal-cnt').text()[:-3], # 从class deal-cnt 里面获取 text(去掉后面3个字符)
            'title': item.find('.title').text(),  # 从名称为 title 的class 里面获取 text
            'shop': item.find('.shop').text(), #从 名称为 shop 的class里面获取 text
            'localtion': item.find('.location').text() #从名称为 localtion 的 class 里面获取 text
        }
        print(product)   # 打印单个商品信息
        save_to_mongo(product)  #保存单个商品信息到 MONGODB


def save_to_mongo(result):  # 定义保存到 MONGODB 的函数,传入 result
    try:  # 错误判断
        if db[MONGO_TABLE].insert(result):
            print('保存到 MONGO_DB成功', result)
    except Exception:  #如果出错就答应下面的文字+ result(result 是 product)
        print('存储到mongodb 错误', result)


def main():
    try:
        total = search()
        total = int(re.compile('(\d+)').search(total).group(1))
        for i in range(2, total + 1): # 页码从第2页开始,然后是3、4、5。因为一打开就是第1页
            next_page(i)
    except Exception:   #如果出错就给错误信息
        print('错误啦')
    finally: # 不论是否出现异常,最后都要执行下面的操作
        browser.close()


if __name__ == '__main__':
    main()

config.py

MONGO_URL = 'localhost'
MONGO_DB = 'taobao'
MONGO_TABLE = 'product'

SERVICE_ARGS = ['--load-images=false', '--disk-cache=true']
# http://phantomjs.org/api/command-line.html


KEYWORD = '美食'
Comments
Write a Comment