一、首先我们分析一下京东站点:
要是想爬取美食的话,我们首先需要在搜索输入框里输入我们要搜索的内容,也就是“美食”两个字,然后点击搜索按钮,这个技术我们需要用selenium来实现模拟操作,我们用一个search函数来实现
def search():
try:
# 我们要确保所有元素加载完成,所以需要一个判断
# 谷歌搜索python selenium进入官网,我们可以看到左侧有一个waits,点击进去,看到下面的注释内容,可以学习一下
# 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
#
# driver = webdriver.Firefox()
# driver.get("http://somedomain/url_that_delays_loading")
# try:
# element = WebDriverWait(driver, 10).until(
# EC.presence_of_element_located((By.ID, "myDynamicElement"))
# )
# finally:
# driver.quit()
#搜索框
input = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#key'))
)
#提交按钮
submit=wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR,'#search > div > div.form > button'))#去网页F12然后选中
#搜索按钮,然后右键copy-copy selector即可复制到#J_TSearchForm > div.search-button > button
)
#模拟操作 输入,提交
input.send_keys('美食')
submit.click()
# 等到已经翻页成功
wait.until(
EC.text_to_be_present_in_element((By.CSS_SELECTOR, "#J_bottomPage > span.p-num > a.curr"), '1'))
browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
#wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#J_goodsList > ul > li:last-child')))
sleep(2)
get_products()
# 返回一个美食的页数
total = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "#J_bottomPage > span.p-skip > em:nth-child(1) > b")))
return total.text
except TimeoutError:#如果超时,反复执行
return search()
我想解释一下其中的一些代码,比如browser.execute_script('window.scrollTo(0,0.8*document.body.scrollHeight)')
京东美食搜索后,每页总共可以显示60个商品,但是不是一次性加载的,当进度条拉到一半的时候才会加载另外三十个商品,所以,我们这行代码就是模拟浏览器的下拉滚动条操作,sleep(2)等待两秒,等待加载完成,然后调用了一个函数get_products(),就是获取页面的六十个商品的信息的函数,下面会详细介绍。然后这个search函数会获取美食的总页数,然后返回。
二、模拟翻页操作:
哎,由于京东不是一次性加载60个商品,多了很多不必要的代码来抵消加载另外三十个商品时的页面刷新,所以代码有些丑陋
def next_page(page_num):
print('--------------------------------------正在翻页----------------------------------------')
sleep(3)
try:
browser.execute_script('window.scrollTo(0,0.8*document.body.scrollHeight)')
sleep(1)
input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#J_bottomPage > span.p-skip > input")))
submit=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,"#J_bottomPage > span.p-skip > a")))
input.clear()
input.send_keys(page_num)
submit.click()
#等到已经翻页成功
wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR,"#J_bottomPage > span.p-num > a.curr"),str(page_num)))
browser.execute_script('window.scrollTo(0,0.8*document.body.scrollHeight)')
sleep(1)
get_products()
except TimeoutError:
next_page(page_num)
三秒睡眠等待三十组商品已经加载完成,然后模拟浏览器滚动条下拉,来激活另外三十组商品的加载,然后睡眠一秒,然后等待并获取input和submit,这两个是什么呢,其实就是翻页的输入框和确定按钮:
获取到了然后就可以更改内容,并翻页了,翻页成功自然也要抓取商品了,但是依然需要模拟下拉滚动条来激活另外三十组商品,就很烦,然后睡眠一秒等待加载完成之后就可以获取商品了,即调用get_products函数
三、获取商品(需要用pyquery来解析网页)
看一下代码吧
#解析
def get_products():
#保证所有商品已经加载完了
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,"#J_goodsList > ul > li:last-child")))
#获取网页源码
html=browser.page_source
#解析
doc=pq(html)
items=doc('#J_goodsList .gl-warp.clearfix .gl-item').items()
for index,i in enumerate(items):
#print(i('.p-name.p-name-type-2 em').text())
if i('.p-img a').attr('href')[0]=='/':
ss='https:'+i('.p-img a').attr('href')
else:
ss=i('.p-img a').attr('href')
product={
'index':index,
'price':i('.p-price i').text(),
'name':i('.p-name.p-name-type-2 em').text().replace('\n',' '),
'store':i('.J_im_icon a').text(),#店名
'url':ss,
# 'img':'https:'+i('.p-img img').attr('src')#貌似图片爬不下来
}
print(product)
#存入数据库
print('----------------------------------------------存入数据库-----------------------------------------------')
try:
collection.insert_one(product)
except Exception:
print("--------数据库操作出错-------")
首先获取网页源码,然后用pyquery解析,for循环里就是解析的方法,当然以后京东更改前端代码的话这个内容也是需要改变的,目前还是好使的。大家可以看一下京东的前端代码:
四、存入数据库的操作
python操作mongodb还是比较方便的,我这里写了一个配置文件
#config_mongodb.py
from pymongo import MongoClient
conn=MongoClient("127.0.0.1",27017)#默认端口
#链接数据库
db=conn.爬取京东数据
#获取集合
collection=db.Products
#插入一条
#collection.insert_one({"name": "笑笑2", "age": 5, "gender": 0, "address": "静安", "isDelete": 0})
#断开连接
#conn.close()
#phantom.js的使用,不需要再打开浏览器什么的了
Service_args=['--load-images=false','--disk-cache=true']
这一块我觉得大家上网搜一下代码就好了,还是比较简单的。
五、拓展
如果不想每次运行都要打开浏览器然后自动模拟各种操作的话怎么办呢?需要用到phantomJs,大家可以上网下载一下,顺便看一看说明文档,里面告诉我们怎么用了。
1.配置文件里写一下一个参数:
Service_args=['--load-images=false','--disk-cache=true']
意思是不用加载浏览器的图片,同时开启缓存。
然后将原来的browser=webdriver.Chrome()改为
browser=webdriver.PhantomJS(r'E:\phantomjs-2.1.1-windows\bin\phantomjs.exe',service_args=Service_args)
六、完整代码:
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 time import sleep
import os
from pyquery import PyQuery as pq
from config_mongodb import *
#browser=webdriver.Chrome()
#如果需要不用打开浏览器,则注释掉上一行,执行下一行
browser=webdriver.PhantomJS(r'E:\phantomjs-2.1.1-windows\bin\phantomjs.exe',service_args=Service_args)
#s设置窗口大小
#browser.set_window_size(1400,900)
browser.get('https://www.jd.com/')
wait=WebDriverWait(browser, 10)
#第一步 在搜索框输入要搜索的内容
def search():
try:
# 我们要确保所有元素加载完成,所以需要一个判断
# 谷歌搜索python selenium进入官网,我们可以看到左侧有一个waits,点击进去,看到下面的注释内容,可以学习一下
# 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
#
# driver = webdriver.Firefox()
# driver.get("http://somedomain/url_that_delays_loading")
# try:
# element = WebDriverWait(driver, 10).until(
# EC.presence_of_element_located((By.ID, "myDynamicElement"))
# )
# finally:
# driver.quit()
#搜索框
input = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#key'))
)
#提交按钮
submit=wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR,'#search > div > div.form > button'))#去网页F12然后选中
#搜索按钮,然后右键copy-copy selector即可复制到#J_TSearchForm > div.search-button > button
)
#模拟操作 输入,提交
input.send_keys('美食')
submit.click()
sleep(2)
# 等到已经翻页成功
wait.until(
EC.text_to_be_present_in_element((By.CSS_SELECTOR, "#J_bottomPage > span.p-num > a.curr"), '1'))
browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
#wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#J_goodsList > ul > li:last-child')))
sleep(2)
get_products()
# 返回一个美食的页数
total = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "#J_bottomPage > span.p-skip > em:nth-child(1) > b")))
return total.text
except TimeoutError:#如果超时,反复执行
return search()
def next_page(page_num):
print('--------------------------------------正在翻页----------------------------------------')
sleep(3)
try:
browser.execute_script('window.scrollTo(0,0.8*document.body.scrollHeight)')
sleep(1)
input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#J_bottomPage > span.p-skip > input")))
submit=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,"#J_bottomPage > span.p-skip > a")))
input.clear()
input.send_keys(page_num)
submit.click()
#等到已经翻页成功
wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR,"#J_bottomPage > span.p-num > a.curr"),str(page_num)))
browser.execute_script('window.scrollTo(0,0.8*document.body.scrollHeight)')
sleep(1)
get_products()
except TimeoutError:
next_page(page_num)
#解析
def get_products():
#保证所有商品已经加载完了
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,"#J_goodsList > ul > li:last-child")))
#获取网页源码
html=browser.page_source
#解析
doc=pq(html)
items=doc('#J_goodsList .gl-warp.clearfix .gl-item').items()
for index,i in enumerate(items):
#print(i('.p-name.p-name-type-2 em').text())
if i('.p-img a').attr('href')[0]=='/':
ss='https:'+i('.p-img a').attr('href')
else:
ss=i('.p-img a').attr('href')
product={
'index':index,
'price':i('.p-price i').text(),
'name':i('.p-name.p-name-type-2 em').text().replace('\n',' '),
'store':i('.J_im_icon a').text(),#店名
'url':ss,
# 'img':'https:'+i('.p-img img').attr('src')#貌似图片爬不下来
}
print(product)
# #存入数据库
# print('----------------------------------------------存入数据库-----------------------------------------------')
# try:
# collection.insert_one(product)
# except Exception:
# print("--------数据库操作出错-------")
def main():
total=search()
for i in range(2,int(total)+1):
next_page(i)
if __name__=='__main__':
main()
七、结果展示(只爬了两页)
如有错误,还请指正~!