本次爬取的内容为京东的搜索功能。通过selenium利用驱动启动浏览器,并输入搜索内容,并模拟切换页面、滚动页面等操作抓取搜索结果。保存搜索商品结果的标识、描述、价格、商家、图片地址及商品链接到本地数据库中。
爬取过程
首先设置浏览器(无头浏览器可不设——设置目的为了提高效率及尽可能少的占用内存) -- 使用驱动启动浏览器 -- 设置启动的浏览器的界面大小及等待页面内容加载的时间 -- 通过多次观察发现京东的搜索结果均为100页 -- 主函数中通过循环生成每次填入的页码 -- 将页码传递给模拟切换页面的函数 -- 模拟切换页面的函数判断页码是否为一,若为一则向页面写入经过编码的搜索内容到请求地址中,若不为一则先得到页码输入框和确认按钮,之后清空页码输入框(避免输入页码追加到原有页码之后)输入得到的页码,点击确认按钮 -- 使用循环分六次滚动刷新页面(避免页面内容缺省太多) -- 返回当前页面内容 -- 解析页面内容(首先将所有拿到的li标签强制转换为一个list,通过循环该列表拿到单个商品的li标签。再使用.
匹配li标签中的内容,存为dict形式后添加到商品list中,便于之后插入到数据库中。) -- 连接数据库保存数据 -- 抛商标标识唯一引起的异常
import time
import random
import pymysql
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from urllib.parse import quote
from lxml import etree
# 无头浏览器
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
# 启动浏览器
browser = webdriver.Chrome(chrome_options=chrome_options)
# 指定自启浏览器界面大小
browser.set_window_size(1400, 700)
# 显式等待 针对整个节点的等待
wait = WebDriverWait(browser, 3)
# 设置关键字
KEYWORD = '古风'
# 隐式等待(不推荐使用)
# browser.implicitly_wait(3)
# 模拟切换页面
def get_page(page):
if page == 1:
# 第一次访问的地址
url = 'https://search.jd.com/Search?keyword=%s&enc=utf-8' % quote(KEYWORD)
# 访问地址
browser.get(url)
if page > 1:
# 获取页数输入框
input = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#J_bottomPage input.input-txt')))
# 获取确认按钮
submit = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage a.btn.btn-default')))
# 清空页数输入框
input.clear()
# 将目标页数填入到页数输入框
input.send_keys(page)
# 点击确认按钮
submit.click()
wait.until(
EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#J_bottomPage a.curr'), str(page)))
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_goodsList')))
# 随机等待时间
t = random.randint(0, 9)
# 分六次滚动页面
for i in range(6):
str_js = 'var step = document.body.scrollHeight/6;window.scrollTo(0,step*%d)' % (i + 1)
browser.execute_script(str_js)
time.sleep(t)
# 得到访问的页面内容
page_source = browser.page_source
return page_source
# 解析获取到的页面
def parse_page(page_source):
# 创建xpath解析对象
etree_html = etree.HTML(page_source)
# print(page_source)
# print(type(etree_html))
# 得到单个商品(解决一个商品单个属性中多个信息的选择)
goods_list = list(etree_html.xpath('//div[@id="J_goodsList"]/ul/li'))
# print(len(goods_list))
goods_db = []
for goods in goods_list:
item = {}
# 解析标识
goods_sku = goods.xpath('./@data-sku')
# print(goods_sku[0])
item['sku'] = goods_sku[0]
# 解析描述(.表示当前目录)
goods_title = goods.xpath('.//div[@class="p-name p-name-type-2"]/a/em/text()')
# print(goods)
# print(goods_title)
title = ''
for i in goods_title:
title += i
# print(title)
# print(len(goods_title))
item['title'] = title.replace(' ', '')
# 解析价格
goods_price = goods.xpath('.//div[@class="p-price"]/strong/i/text()')
# print(goods_price[0])
item['price'] = goods_price[0]
# 解析商家
goods_shop = goods.xpath('.//div[@class="p-shop"]/span/a/@title')
# print(goods_shop[0])
item['shop'] = goods_shop[0]
# 解析评价数量
goods_commit = goods.xpath('.//div[@class="p-commit"]/strong/a/text()')
# print(goods_commit[0])
item['commit'] = goods_commit[0]
# 解析图片地址(部分图片加载不到,设置重复更新后多次爬取)
goods_img = goods.xpath('.//div[@class="p-img"]/a/img/@src')
# print(goods_img)
item['img'] = goods_img
if goods_img:
item['img'] = goods_img[0]
else:
item['img'] = ''
# 解析商品链接
goods_link = goods.xpath('.//div[@class="p-img"]/a/@href')
# print(goods_link)
item['link'] = goods_link[0]
# 将解析好的单品信息加入返回结果中
goods_db.append(item)
return goods_db
def join_MySql(goods_db):
# 设置数据库参数
host = '127.0.0.1'
user = 'root'
password = 'root'
port = 3306
db = 'jdSeacrh'
db = pymysql.connect(host=host, user=user, password=password, port=port, db=db)
# 连接数据库
cursor = db.cursor()
for i in range(len(goods_db)):
sql = "INSERT INTO seacrh_gufeng(sku,title,price,shop,commit,img,link)" \
" VALUES('{}','{}','{}','{}','{}','{}','{}')".format(goods_db[i]['sku'], goods_db[i]['title'],
goods_db[i]['price'], goods_db[i]['shop'],
goods_db[i]['commit'],
goods_db[i]['img'], goods_db[i]['link'])
# print(sql)
try:
cursor.execute(sql)
db.commit()
except:
print('元素已存在')
db.close()
def main():
for page in range(100):
# 动态增加页数
page_source = get_page(page + 1)
# 解析页面内容
goods_db = parse_page(page_source)
join_MySql(goods_db)
if __name__ == '__main__':
main()
总结:
京东这一块最麻烦的是解析页面,存的时候总是数据格式出错。也许是因为存储的是MySQL的原因吧!商品描述是em标签下被span分开的一个或多个字符串,导致解析难度提升!还有图片地址的问题到现在还没想到什么方法完美解决,每次加载的时候只有少量的图片地址能够被加载出来。知道应该多次访问,存图片的时候判断该信息是否存在,但又好像和唯一索引冲突。。。很迷...
网友评论