本次爬取的内容为京东的搜索功能。通过selenium利用驱动启动浏览器,并输入搜索内容,并模拟切换页面、滚动页面等操作抓取搜索结果。保存搜索商品结果的标识、描述、价格、商家、图片地址及商品链接到本地数据库中。
爬取过程
首先设置浏览器(无头浏览器可不设——设置目的为了提高效率及尽可能少的占用内存) -- 使用驱动启动浏览器 -- 设置启动的浏览器的界面大小及等待页面内容加载的时间 -- 通过多次观察发现京东的搜索结果均为100页 -- 主函数中通过循环生成每次填入的页码 -- 将页码传递给模拟切换页面的函数 -- 模拟切换页面的函数判断页码是否为一,若为一则向页面写入经过编码的搜索内容到请求地址中,若不为一则先得到页码输入框和确认按钮,之后清空页码输入框(避免输入页码追加到原有页码之后)输入得到的页码,点击确认按钮 -- 使用循环分六次滚动刷新页面(避免页面内容缺省太多) -- 返回当前页面内容 -- 解析页面内容(首先将所有拿到的li标签强制转换为一个list,通过循环该列表拿到单个商品的li标签。再使用.
匹配li标签中的内容,存为dict形式后添加到商品list中,便于之后插入到数据库中。) -- 连接数据库保存数据 -- 抛商标标识唯一引起的异常
import timeimport randomimport pymysqlfrom selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom urllib.parse import quotefrom 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_dbdef 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分开的一个或多个字符串,导致解析难度提升!还有图片地址的问题到现在还没想到什么方法完美解决,每次加载的时候只有少量的图片地址能够被加载出来。知道应该多次访问,存图片的时候判断该信息是否存在,但又好像和唯一索引冲突。。。很迷...
作者:GHope
链接:https://www.jianshu.com/p/ebda022a5fef
共同学习,写下你的评论
评论加载中...
作者其他优质文章