赞
踩
使用selenium爬取猫眼Top100中,电影每部电影的排名,电影名,主演,上映时间及评分。
通过分析页面结构,选择合适的接入点。selenium直接进行页面检查就可以,不用再分析代码是否在源码中出现。
光标位于第一个电影处,点击右键,检查,定位到第一个电影的信息,我们可以把标签折叠,可以看到dd标签里存放了一个电影的所有数据,所有的dd标签里存放了本页面所有的电影,而dd标签都存放于dl标签内。那我们只需要找到dl标签,再从里面找到dd标签就可以获取到电影数据。
滑动网页,可以看到第一页共有十个电影,在代码中刚好由10个dd标签。
总结,通过分析,一个dd标签对应一部影片,所有的dd标签存放于dl标签中,我们需要先找到class="board-wrapper"的dl标签,然后在dl标签下面找到dd标签,最后在dd标签中解析数据。
举例:函数有多个返回值,只取其中需要的返回值的方法
import csv
def test():
return [{'t': 2, 'm': '3'}],['t','m']
a, b = test()
a, _ = test()
_, b = test()
with open('a.c.csv', 'a',encoding='utf-8',newline="") as f:
w = csv.DictWriter(f,b)
w.writeheader()
w.writerows(a)
print(a) [{'t': 2, 'm': '3'}]
print(b) ['t', 'm']
from selenium import webdriver import csv import time driver = webdriver.Edge() driver.get('https://maoyan.com/board/4?offset=0') time.sleep(1) # 找到dl标签 # driver.find_element_by_xpath('//*[@id="app"]/div/div/div[1]/dl') # 找到dl标签下所有的dd标签,因为获取的不是一个,所有用elements # 解析一页的函数 def get_data(): dd_tags = driver.find_elements_by_xpath('//*[@id="app"]/div/div/div[1]/dl/dd') lis = [] for dd_tag in dd_tags: # text用来获取节点(包括子节点和所有后代节点)的所有文本, # print(type(dd_tag.text), dd_tag.text) # 字符串 # 打印出来的内容是用换行分隔的,可以用字符串的分割方法把获取的内容转为列表 lis_dd = dd_tag.text.split('\n') item = {} # 定义titles,在保存函数的时候也作为表头继续使用 titles = ['rank', 'name', 'star', 'time', 'score'] # 在lis_dd 中按索引取出数据 suoyins = range(5) for title, suoyin in zip(titles, suoyins): # title作为key值,suoyin作为value值,保存到字典item中 item[title] = lis_dd[suoyin] # 把保存的每一个字典存放到列表中 lis.append(item) return lis, titles # 返回列表lis用于写入数据,返回tieles用做写入的表头 def save_data(lis_data, titles): with open('data3.csv', 'a', encoding='utf-8',newline="") as f: writ = csv.DictWriter(f, titles) writ.writerows(lis_data) if __name__ == '__main__': # 解决重复表头的问题,写入表头,以后的循环就只写入数据 # 从两个返回值中只取出 titles部分 _, titles = get_data() with open('data3.csv', 'a', encoding='utf-8', newline="") as f: writ = csv.DictWriter(f, titles) writ.writeheader() while True: # 从两个返回值中取出数据部分 lis, _ = get_data() # 把两个返回值传入save_data函数 save_data(lis, titles) time.sleep(3) try: # 前端中a标签通常代表链接,下一页超链接的文本 driver.find_element_by_link_text('下一页').click() # 如果能找到“下一页”,就点击没找不到的话进入except except: # 退出驱动,并终止循环 driver.quit() break
之前讲过无头浏览器(Phantomjs),没有界面,在内存中加载出来的,如果刚才不加载猫眼的话,爬取速度会稍微快一点(打开页面会占用一定的内存),如果对selenium设置了无界面,加载速度会快一点。使用无界面模式最好是在已经通过程序实现了爬取数据的功能,不然出现问题很可能不知道在哪。这里只尝试了使用谷歌浏览器,Edge还没有发现相应的功能,如果下面的代码用于爬取上面的案例中,需要安装谷歌浏览器的驱动。
# 创建chrome设置对象
options = webdriver.ChromeOptions()
# 设置无界面功能
# ---headless 浏览器无界面
options.add_argument('---headless')
# 把options 以传参的形式传入驱动器
driver = webdriver.Chrome(options=options)
任意关键字的搜索数据(input),进入京东界面,输入爬虫书进行检索,把每本书的价格、书名、评价人数、店铺名的结果保存下来。
在第一本书的地方点右键,检查,光标定位到这本书的图片位置,然后一级一级的折叠标签,会发现每一本书都放在一个li标签内,而所有的li标签都放于ul标签内,ul标签内存放本页面所有的书籍。
在网页界面继续下划,会在代码下方出现新加载书的li标签,这就属于懒加载,数据在界面到达的时候才会加载出来,京东界面是分两次加载出来的,第一次只加载了一部分,在页面下划到一定的界面才会加载剩下的数据,这个时候拿url去获取的数据是不全的。
打开页面是加载了一部分的数据,用selenium拿到的数据是不全的,我们可以直接把页面拖到最下面(自动拖动),等待所有的内容都加载完成之后再去做数据的获取。面对懒加载导致数据不全的问题,我们解决的思路是定位到页面后直接把右侧的滚动条拖动到页面底部,给页面留点加载的时间,等页面元素加载完成之后我们再进行数据的获取。可以用鼠标行为链拖动滚动条到最下面,也可以用js代码来实现。
driver.execute_script(‘window.scrollTo(0,document.body.scrollHeight)’)
这里通过execute_scrip来执行js代码,0是起始位置,document.body.scrollHeight是整个窗口的高度,代码实现的功能是把滚动条翻到页面最下方。
下面进行每本书的价格、书名、评价人数、店铺名的查找,通过遍历ul下的li标签,可以看到第二本书比第一本书多了单件/套装2件,优惠券两个内容。
如果直接通过遍历元素的text方法索引取值,得到数据会不一致,通过代码打印的结果可以看到第一本书的价格在第一个内容,而第二本书的价格在第4个内容,这时候就对要查找的内容进行逐个取值。
下面实现翻页的操作,通过观察,可以看到搜索内容一共100页,每一页都有一个下一页按钮,可以通过点击下一页按钮进入下一页,
在100页的时候,下一页按钮是灰色的,不能点击,观察代码中出现了class="pn-next disabled"标签。可以一直点击下一页,直到出现class="pn-next disabled"标签,就停止点击来实现翻页用driver.page_source.find(class=“pn-next disabled”)来实现。
由于爬取的数据比较多,多多少少会出现有些个别的数据有问题,可以把获取属性的数据用try处理一下。
from selenium import webdriver import time import csv class jd_Data(): def __init__(self): self.driver = webdriver.Edge() self.driver.get('https://www.jd.com/') time.sleep(1) self.driver.find_element_by_id('key').send_keys('爬虫书') # 这里要注意iconfont 在代码中有很多个,最好不要用class_name获取 # 简单的说没有id的尽量都用xpath # self.driver.find_element_by_class_name('iconfont').click() self.driver.find_element_by_xpath('//*[@id="search"]/div/div[2]/button').click() time.sleep(1) # 完成数据的爬取和解析 def parse_data(self): # 京东是懒加载,需要先把页面下划到底部,显示所有的数据 # 通过execute_script来执行js代码,0是起始位置,document.body.scrollHeight是整个窗口的高度 self.driver.execute_script('window.scrollTo(0,document.body.scrollHeight)') time.sleep(3) li_list = self.driver.find_elements_by_xpath('//*[@id="J_goodsList"]/ul/li') self.lis_data = [] for li in li_list: # print(li.text) # print('*'*50) # 发现数据分布不均衡,第一本书的价钱在第一个文本内,第二本书前面有单件和套装,优惠信息,第四个文本才是价钱 # 因此不能用li.text分割后的索引获取数据 item = {} # item['price'] = li.find_element_by_xpath('//*[@id="J_goodsList"]/ul/li[1]/div/div[2]/strong') # 上面直接用右键复制xpath的方式 # 也可以直接去找xpath,价格是在(class="p-price)的div下的strong标签内 # 使用驱动找到li_list后,下面寻找的各项数据是在li中找到,不需要再用self.driver去找数据 try: item['price'] = li.find_element_by_xpath('.//div[@class="p-price"]/strong').text.strip() item['name'] = li.find_element_by_xpath('.//div[@class="p-name"]/a/em').text.strip() item['commit'] = li.find_element_by_xpath('.//div[@class="p-commit"]/strong').text.strip() item['shopnum'] = li.find_element_by_xpath('.//div[@class="p-shopnum"]/a').text.strip() print(item) self.lis_data.append(item) except Exception as e: print(e) def save_data(self): with open('jd_data.csv', 'a', encoding='utf-8', newline="") as f: writ = csv.DictWriter(f, self.header) writ.writerows(self.lis_data) def main(self): self.header = ['price', 'name', 'commit', 'shopnum'] with open('jd_data.csv', 'a', encoding='utf-8', newline="") as f: writ = csv.DictWriter(f, self.header) writ.writeheader() while True: self.parse_data() self.save_data() # 在网页源码中查找数据-1代表没有找到内容,也就代表不是最后一页 # page_source.find在代码中查找元素 if self.driver.page_source.find("pn-next disabled") == -1: self.driver.find_element_by_xpath('//*[@id="J_bottomPage"]/span[1]/a[9]').click() time.sleep(3) else: self.driver.quit() break if __name__ == '__main__': jd = jd_Data() jd.main()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。