爬虫的步骤
-
什么是爬虫(spider)
爬虫就是通过代码取获取别人网站上的数据
-
爬虫的基本步骤
-
获取网站数据 - - - -
requests
和自动化测试工具(Selenium)
-
认识网站
''' 一个网页由三个部分组成:html、css、javascript(js) html - 决定网页中有什么 css - 决定网页中内容的样式(长什么样) js - 让网页内容动态变化 '''
-
requests的使用
import requests from re import findall # 1.获取网页内容 # requests.get(url) - 获取指定url对应的网页内容,返回一个响应 # url - 同一资源定位符(网址) response = requests.get('https://www.sohu.com/') print(type(response), response) # <class 'requests.models.Response'> <Response [200]> # 2. 获取网页内容 # 1) 获取网页内容原数据(类型是二进制) - 主要针对图片、视频、音频 print(response.content) # 2)获取网页内容文本格式数据 - 针对网页 print(response.text) # 3)将获取的内容进行json转换 - 针对返回数据是json数据的接口 print(response.json()) # 3.添加客户端信息 - 伪装成浏览器 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', 'cookie': '_zap=39beb782-a6cf-4483-8719-8e2d846da00d; _xsrf=zMuRHL0ug0AKhKYfxromKD0K2pb49WzV; d_c0="AMDePoka3xKPToqc24ryAdbkUVa-BnMNCLc=|1616989878"; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1616989879; SESSIONID=JoqxQeb0aYuI0CciYGx0er5NN2MwIbEn2MoqPCwApGO; 87e910261ed42a34360ce1c69f0"; z_c0="2|1:0|10:1616990313|4:z_c0|92:Mi4xaW5CWUdRQUFBQUFBd040LWlScmZFaWNBQUFDRUFsVk5hTjJJWUFDN0NBVmJOZXZhMHotdG9QU1R1cXFnU0NNS1J3|a1c1166e239c385fea71a694d583b66f40dfcd23e553192253270520e98d6cfc"; tst=h; tshl=; Hm_lpvt_98beee57fd2ef70ccdd5ca52b9740c49=1617003964; KLBRSID=53650870f91603bc3193342a80cf198c|1617003965|1617003881' } response = requests.get('https://www.zhihu.com/', headers=headers) print(response.headers) print(response.text)
-
淘宝设置cookie
from selenium.webdriver import Chrome from selenium.webdriver import ChromeOptions from selenium.webdriver.common.keys import Keys import time def get_cookies(url, file_name): """ 通过selenium获取指定网站的cookie,并将获取到的cookie保存到指定文件中 :param url: 指定网站的网址 :param file_name: 保存cookie的文件 :return: None """ wb = Chrome() wb.get(url) # 给手动登录的时间 time.sleep(60) cookies = wb.get_cookies() print(type(cookies), cookies) with open(file_name, 'w') as f: f.write(str(cookies)) def open_taobao(): # 1. 创建设置对象 options = ChromeOptions() # 取消自动化测试环境提示 options.add_experimental_option('excludeSwitches', ['enable-automation']) # 取消图片的加载 options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2}) # 2.创建浏览器对象 wb = Chrome(options=options) wb.get('https://www.taobao.com') # 添加cookie with open('cookies/taobao.txt') as f: cookies = eval(f.read()) for x in cookies: wb.add_cookie(x) time.sleep(1) wb.get('https://www.taobao.com') search_input = wb.find_element_by_css_selector('#q') search_input.send_keys('橙子') search_input.send_keys(Keys.ENTER) print(wb.page_source) time.sleep(10) open_taobao()
-
京东滚动
from selenium.webdriver import Chrome from selenium.webdriver import ChromeOptions from selenium.webdriver.common.keys import Keys import time options = ChromeOptions() options.add_experimental_option('excludeSwitches', ['enable-automation']) options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2}) wb = Chrome(options=options) wb.get('https://www.jd.com') search_input = wb.find_element_by_css_selector('#key') search_input.send_keys('海贼王') search_input.send_keys(Keys.ENTER) time.sleep(2) # 滚动 wb.execute_script('window.scrollTo(0, document.body.scrollHeight - 600)') # wb.execute_script('alert("To Bottom")') print(wb.page_source) next_btn = wb.find_element_by_css_selector('.pn-next') print('===================================================') print(next_btn) # next_btn.click() time.sleep(10) wb.close()
-
Selenium的使用
from selenium.webdriver import Chrome from selenium.webdriver.common.keys import Keys # 回车 import time # 1.创建浏览器对象(谷歌) wb = Chrome() # 2. 打开指定网页 wb.get('https://www.baidu.com') # 3.获取当前浏览器中,显示的页面的网页源代码 print(wb.page_source) # 4.获取网页中的标签,并且操作标签(输入框,) # 浏览器对象..find_element_by_css_selector(css选择器) # 根据选择器 找到对应的标签 search_input = wb.find_element_by_css_selector('#kwdselectid') # 向输入框中输入内容 输入框标签.send_keys(输入的内容) search_input.send_keys('数据分析') # 输入数据分析 search_input.send_keys(Keys.ENTER) # 回车 next_btn = wb.find_element_by_css_selector('.next') # 下一页 标签 #点击标签: 标签.click() next_btn.click()
-
-
解析数据 - - - 正则表达式、css选择器、xpath, pyquery
-
正则表达式
-
css选择器
# html代码 以下 ''' <!-- css(层叠样式表)是用来设置标签的样式和布局 1.css代码写在哪 内联样式表: 写在标签的style属性中 内部样式表: 写在style标签中 外部样式表: 写在css文件中 2. 怎么写 选择器{ css属性1=值1; css属性2=值2; } 3.css选择器 1)元素选择器 - 直接将标签名作为选择器,选中所有指定名的标签 P -- 选中所有的p标签 2)id选择器 --- 在id属性值之前加#作为一个选择器 #sp1 --- 选中id属性的的值是sp1的标签 3) class选择器 --- 在class属性前加. 作为一个选择器,选择class属性值指定值的所有标签 .c1 - 选择class属性的值是c1所有的标签 4) 群组选择器:选择器1,选择器2,选择3... p,a -- 选择所有的p标签和a标签 p,#sp1 - 选中所有的p标签和所有id是sp1的标签 5) 后代选择器 :选择器1 选择器2 选择3... p a ---- 选中p标签中的a标签 6)子代选择器:选择器1>选择器2>选择3.. p>a -- 选择是p标签子代的a标签 7) p>a:frist 选择第一个 p>a:nth-child(N) --- 获取p标签中第N个子代的标签 --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> /* css语法代码 */ /* 元素选择器 */ a{ color: #008000; font-size: 35px; } /* id选择器 */ #sp1{ color: #FFFF00; } /* 群组选择器 */ a,#sp1{ background-color: azure; } /* css选择器 */ .c1{ color: #FF0000; } </style> </head> <body> <!-- 1.常见的文本标签 --> <!-- p标签 - 会换行--> <p style="color: #008000;">1段文字</p> <!-- 内联样式表 --> <!-- span标签 - 同行显示 --> <span id="sp1">span文字1</span> <span class="c1">span文字2</span> <!-- /* css选择器 */ --> <!-- div标签 (主要用来对标签进行分组)--> <div> <span class="c1">span文字1</span> <!-- /* css选择器 */ --> <span>span文字2</span> </div> <!-- 图片标签 单标签 --> <img src="" title="索隆"/> <!-- 超链接 --> <a href="https:\\www.baidu.com">可见部分</a> <!-- 输入框 --> <input type="text" name="" id="" value="张三" placeholder="请输入账号" /> <!-- 文本 --> <input type="password" name="" id="" value="张三" placeholder="请输入密码" /> <!-- 密码 --> <input type="button" name="" id="" value="张三" /> <!-- 按钮 --> <input type="checkbox" name="" id="" value="张三" /> <!-- 多选 --> <!-- 按钮 --> <button> 取消</button> </body> </html> '''
-
xpath
(from lxml import etree)-
xpath
的解析
from lxml import etree
xPath主要针对html文件和xml文件,解析原理:通过告诉解析器需要标签在页面中的路径来获取对应的标签
xml也是一种通用的数据格式
0.准备网页数据
with open(‘files/data.html’, encoding=‘utf-8’) as f:
content = f.read()1.创建解析器对象
根节点 = etree.HTML(html文本数据)
html = etree.HTML(content) # <Element html at 0x1045b5e80>
2.获取节点(获取标签)
节点对象.xpath(路径)
1)标签名 - 在当前节点下找对应的子节点(相对路径)
获取html节点中名字叫body的子节点
result1 = html.xpath(‘body’)
print(result1) #[<Element body at 0x105501340>]result2 = html.xpath(‘body/div’)
print(result2) # [<Element div at 0x10c8a23c0>, <Element div at 0x10c8a2440>, <Element div at 0x10c8a2480>]result3 = html.xpath(‘body/div/img’)
print(result3) # [<Element img at 0x10e3115c0>]div = result2[0]
print(div.xpath(‘a’))2). - 写相对路径
result4 = html.xpath(’./body/div/font’)
print(result4)3)/ - 绝对路径(从根节点开始写路径,而且和谁去.xpath无关)
result6 = html.xpath(’/html/body/div/img’)
print(result6) # [<Element img at 0x10c78f1c0>]result7 = div.xpath(’/html/body/div/img’)
print(result7) # [<Element img at 0x10c78f1c0>]4) // - 从任意位置开始
//img - 获取整个页面中所有的img节点
//div/img - 获取整个页面中是div子节点的img节点
result8 = html.xpath(’//p’)
print(result8)result9 = html.xpath(’//div/div/p’)
print(result9)5) … - 当前节点的上层节点
result11 = div.xpath(’…/ol’)
print(result11)6) @ - 获取属性值
//img/@src - 获取整个页面中所有图片标签的src属性
img = html.xpath(’//img/@src’)
print(img)7) text() - 获取标签的文本内容
lis = html.xpath(’//li/text()’)
print(lis)2. ``xpath``的谓词 - - - xPath的谓词可以理解成筛选条件 ```python from lxml import etree # xPath的谓词可以理解成筛选条件,写的时候必须放在[]里面 # 0.准备网页数据 with open('files/data.html', encoding='utf-8') as f: content = f.read() html = etree.HTML(content) # 1.位置 # [N] - 获取第N个(同层的第N个) result = html.xpath('//div[1]') print(result) # 2.属性 # [@属性] - 筛选出包含指定属性的标签 # //p[@id] - 获取设置了id属性的p标签 result = html.xpath('//p[@id]/text()') print(result) #['我是段落2', '我是段落3'] # [@属性=值] - 筛选出指定属性是指定值的标签 # //p[@id="p1"] - 获取id是p1的p标签 result = html.xpath('//p[@id="p1"]/text()') print(result) # ['我是段落2'] # 子标签内容 - 通过子标签的内容来对父标签进行筛选 # //div[p="我是段落5"] - 获取包含内容是"我是段落5"的p标签的div result = html.xpath('//div[p="我是段落5"]') print(result) result = html.xpath('//div[span>90]/p/text()') print(result) # 3.通配符 # 用*表示所有 result = html.xpath('//div[@id="div1"]/*') print(result) result = html.xpath('//*[@*]') print(len(result)) # 4.同时选取多个路径 # 路径1|路径2|路径3|... result = html.xpath('//div[@id="div1"]/p/text()|//div[@id="div1"]/a/text()') print(result)
-
-
bs4
中的BeautifulSoup
使用方法 ---- BeautifulSoup是基于css选择器的解析库-
准备数据(一般是通过requests或者selenium爬取到的)
from bs4 import BeautifulSoup
#准备爬取到的数据html2. 创建BeautifulSoup解析器对象 ```python # BeautifulSoup(解析对象, 解析器类型) # 解析对象 - 一般是html格式字符串(一般是通过requests或者selenium爬取到的) # 解析器类型 - lxml(最常用,需要安装c语言库(安装lxml python的库就行))、html.parser、xml、html5lib soup = BeautifulSoup(content, 'lxml')
-
获取标签 -
css选择器
- 通过css选择器(
解析对象.select(css选择器)
)``
# 解析器对象.select(css选择器) - 在整个页面中按照css选择器查找指定标签 # 标签对象.select(css选择器) - 在当前标签中按照css选择器查找指定标签 result = soup.select('p') print(result) result = soup.select('#f1') print(result) result = soup.select('div>p') print(result) # 嵌套写法: 标签1.标签2.标签3.... -> 获取标签1中第一个标签2中的第一个标签3... div = soup.select('div')[0] print(div.p) ol = soup.select('#o1')[0] print(ol) print(ol.li) print(ol.select('li')) lis = soup.select('#o1>li') print(lis)
- 按照指定属性值查找标签(
解析对象.find_all('div',class_="item")
)
# 获取href属性值是'https://www.jd.com'的标签 result = soup.find_all(attrs={'href': 'https://www.jd.com'}) print(result) # 获取tag属性是'hot'的所有标签 result = soup.find_all(attrs={'tag': 'hot'}) print(result) # 获取tag属性是'hot'并且height属性是'100'的所有标签 result = soup.find_all(attrs={'tag': 'hot', 'height': '100'}) print(result)
- 获取内容和属性(
标签.string
和标签.contents
和标签.get_text()
)
# 标签.string - 获取双标签的文字内容(注意:被获取的标签中不能有子标签,否则结果是None) # 标签.contents - 获取双标签的内容(包括文字内容和子标签) b1 = soup.select('#f1')[0] print(b1.string) # 我是font1 b2 = soup.select('#f2')[0] print('===:', b2.string) # ===: None print(b1.contents) # ['我是font1'] print(b2.contents, b2.contents[-1].string) # ['我是font2 ', <a href="#">abc</a>] abc print(b1.get_text()) # 我是font1 print(b2.get_text()) # 我是font2 abc
- 获取标签属性(
标签对象.attrs[属性名]
)
# 标签对象.attrs[属性名] b3 = soup.body.div.a print(b3.attrs['href']) # https://www.baidu.com b4 = soup.find_all(attrs={'title': 't百度'})[0] print(b4.attrs['src'], b4.attrs['title']) # https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png t百度
- 通过css选择器(
-
-
-
存储数据 - - - 表格文件(csv、openpyxl(Excel))和数据库文件
-
csv
-
openpyxl
(Excel) - - - -import openpyxl
-
Excel的读操作
import openpyxl # 1.加载excel文件 work_book = openpyxl.load_workbook('files/example.xlsx') # 2.获取表相关数据 # 1) 获取表名 names = work_book.sheetnames print(names) # ['排行榜', '学生信息表', '学生信息表1'] # 2) 获取表对象 # a.根据表名获取工作表对象 sheet1 = work_book['排行榜'] print(sheet1) # <Worksheet "排行榜"> # b.获取活动表 sheet2 = work_book.active print(sheet2) # <Worksheet "排行榜"> # c.获取所有表 all_sheet = work_book.worksheets print(all_sheet) # [<Worksheet "排行榜">, <Worksheet "学生信息表">, <Worksheet "学生信息表1">] sheet3 = all_sheet[-1] print(sheet3) # <Worksheet "学生信息表1"> # 3. 根据表获取表相关信息 # a. 获取表名 print(sheet1.title) # '排行榜' # b.获取最大的行数和最大列表 # 表对象.max_row # 表对象.max_column max_row = sheet1.max_row max_column = sheet1.max_column print(max_row, max_column) # 4.获取单元格 # a.获取一个单元格 # 表对象['标号'] - 标号格式:'字母列表数字行号' cell1 = sheet1['A1'] print(cell1) # <Cell '排行榜'.A1> # 表对象.cell(行号, 列号) - 行号和列号都是从1开始的数字 cell2 = sheet1.cell(3, 2) print(cell2) # b.获取部分单元格 # 一行一行的取 cells1 = sheet1.iter_rows() # 一行一行的获取整个列表中所有的单元格 print(list(cells1)) cells2 = sheet1.iter_rows(min_row=4, max_row=9, min_col=1, max_col=2) print(list(cells2)) # 一列一列的取 cells3 = sheet1.iter_cols() # print(list(cells3)) scores_cell = sheet1.iter_cols(min_row=2, min_col=3) print(list(scores_cell)) # 切片 # 表格对象[左上角标号:右下角标号] cells4 = sheet1['b3':'C9'] print(cells4) # 5. 获取单元格信息 # 1)获取单元格中的数据 print(cell1.value) # 2)获取位置信息 print(cell1.row) # 1 print(cell1.column) # 1 print(cell1.coordinate) # 'A1' for x in cells3: for cell in x: print(cell.value)
-
Excel的写操作
import openpyxl # 1. 打开或创建新的excel文件 # 1)新建excel文件 # 创建工作簿对象 # new_wb = openpyxl.Workbook() # 保存文件: 工作簿对象.save(路径) # new_wb.save('files/new_ex.xlsx') # 2.新建和删除表 wb = openpyxl.load_workbook('files/new_ex.xlsx') # 1) 新建表: 工作簿对象.create_sheet() # new_sheet1 = wb.create_sheet() # new_sheet2 = wb.create_sheet('房屋信息', 1) # new_sheet3 = wb.create_sheet('商品信息', 0) # 2) 删除表: 工作簿对象.remove(表对象) # wb.remove(wb['Sheet']) # 3) 修改表名: 表对象.title = 新名字 # wb['Sheet1'].title = '资讯' # 3.操作单元格 # 1) 修改单元格内容: 表对象['标号'] = 值 sheet1 = wb['商品信息'] # sheet1['A1'] = '名称' # sheet1['B1'] = '价格' # sheet1['B2'] = '' # 修改单元格内容: 单元格对象.value = 值 # sheet1.cell(1, 3).value = '数量' # 2) 删除行:表对象.delete_rows(开始的行号, 需要删除的数量) # wb.active.delete_rows(2, 3) # 3) 删除列:表对象.delete_cols(开始的列号, 需要删除的数量) # wb.active.delete_cols(3, 1) wb.save('files/new_ex.xlsx')
-
-
数据库文件
-
-
代理的使用(
requests
和selenium
的代理使用)
from selenium.webdriver import Chrome
from selenium.webdriver import ChromeOptions
import requests获取代理ip
def get_ips():
url = ‘http://piping.mogumiao.com/proxy/api/get_ip_bs?appKey=6226c130427f487385ad7b5235bc603c&count=2&expiryDate=0&format=2&newLine=3’
response = requests.get(url)
if response.text[0] == ‘{’:
return None
result = [x for x in response.text.split(’\n’) if x]
return resultselenium的代理使用
ips = get_ips()
if ips:
options = ChromeOptions()
# 添加代理
# option.add_argument(’–proxy-server=http://代理ip:端口’)
options.add_argument(f’–proxy-server=http://{ips[0]}’)
b = Chrome(options=options)
b.get(‘https://movie.douban.com/top250’)
else:
print(‘获取ip失败!’)5. selenium控制页面滚动 ```python from selenium.webdriver import Chrome from selenium.webdriver.common.keys import Keys import time b = Chrome() b.get('https://www.jd.com/') search = b.find_element_by_css_selector('#key') search.send_keys('手机') search.send_keys(Keys.ENTER) time.sleep(1) # 一步到位,滚动到底部 # b.execute_script('window.scrollTo(0, document.body.scrollHeight)') # 一点一点的滚动: 500 -> 修改成每次需要滚动的距离,单位是像素; 1000 -> 每次滚动的时间间隔,单位是毫秒 b.execute_script(""" height = 500 t = setInterval(function(){ if (height > document.body.scrollHeight){ clearInterval(t) } window.scrollTo(0, height) height += 500 }, 1000) """)
-
-
pyecharts的使用(图形绘画(柱状图,饼图等等))
from pyecharts.charts import Bar, Line,Kline from pyecharts.options import TitleOpts,LegendOpts,ToolboxOpts,DataZoomOpts, LabelOpts, MarkPointOpts, MarkPointItem, MarkLineOpts,MarkLineItem, AxisOpts,SplitAreaOpts, AreaStyleOpts import openpyxl from datetime import datetime def get_data(): wb = openpyxl.load_workbook('files/腾讯2017年股票数据.xlsx') sheet = wb.active result = sheet.iter_rows(min_row=2, max_col=5) datas = [] for row in result: data = [] for cell in row: data.append(cell.value) time = data[0] data[0] = f'{time.year}-{time.month}-{time.day}' datas.append(data) dates = [] prices = [] for x in datas: dates.append(x[0]) prices.append(x[1:]) return dates, prices # print(get_data()) # datetime.datetime(2017, 1, 3, 0, 0) def create_kline(): kline = Kline() date, price = get_data() # kline.add_xaxis(['2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009']) # kline.add_yaxis('', [[四个数据]]) kline.add_xaxis(date) kline.add_yaxis('', price) kline.set_global_opts( datazoom_opts=DataZoomOpts(is_show=True), yaxis_opts=AxisOpts( is_scale=True, splitarea_opts=SplitAreaOpts( is_show=True, areastyle_opts=AreaStyleOpts(opacity=1) ) ) ) kline.render('files/k线图.html') create_kline() # 2.折线图 def create_line(): line = Line() line.add_xaxis(['成都', '重庆', '北京', '武汉', '深圳', '上海', '昆明']) line.add_yaxis('确诊人数', [120, 98, 456, 2837, 897, 1020, 34], markline_opts=MarkLineOpts( data=[MarkLineItem(type_='average', name='平均值')] ) ) line.add_yaxis('死亡人数', [90, 50, 102, 340, 201, 290, 5]) line.render('files/line.html') # create_line() # 1.柱状图 def create_bar(): # 1.创建图表对象 bar = Bar() # 2.添加数据 # 设置x轴上显示的内容 bar.add_xaxis(['成都', '重庆', '北京', '武汉', '深圳', '上海', '昆明']) bar.add_yaxis('确证人数', [120, 98, 456, 2837, 897, 1020, 34]) bar.add_yaxis('死亡人数', [90, 50, 102, 340, 201, 290, 5]) # 3.设置(可以没有) # 全局配置 bar.set_global_opts( # 设置标题 title_opts=TitleOpts(title='全国疫情信息表', subtitle='确诊人数和死亡人数', pos_left=50), # 隐藏图例 legend_opts=LegendOpts(is_show=False), # 显示工具箱 toolbox_opts=ToolboxOpts(is_show=True), # 显示缩放工具 datazoom_opts=DataZoomOpts(is_show=True) ) # 系列配置 bar.set_series_opts( label_opts=LabelOpts( # 是否显现数据 is_show=True, position='left' ), # markpoint_opts=MarkPointOpts([ # MarkPointItem(type_='max', name='最大值'), # MarkPointItem(type_='min', name='最小值') # ]) ) # 4.渲染 bar.render('files/bar.html') # create_bar()