Python数据分析

大部分内容整理自书籍《利用Python进行数据分析》

github

数据分析的前提是得有数据,所以先整理下Python爬虫相关的内容

Python爬虫

参考网址

requests

BeautifulSoup

xpath

selenium

Scrapy

Python爬虫三部曲

  1. 使用Requests库来抓取网页信息
  2. 使用XPath解析内容。XPath是XML Path的缩写,也就是XML路径语言。在开发中经常用来当作小型查询语言。XPath可以通过元素和属性进行位置索引
  3. 使用Pandas保存数据。Pandas是让数据分析工作变得更加简单的高级数据结构,我们可以用Pandas保存爬取的数据。最后通过Pandas再写入到EXCEL或者MySQL等中

当然做Python爬虫还有很多利器,比如Selenium、PhantomJS或者用Puppeteer这种无头模式

requests库

pip3 install requests

作者又基于requests库封装了一个使用更方便的requests-html库

import requests

# GET请求
url = 'http://127.0.0.1:9501/test'
print(requests.get(url, timeout=3).text)

# 模拟浏览器请求
url = 'http://local.test.com/admin/public/test'
headers = {
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36'
}
print(requests.get(url, timeout=3, headers=headers).text)

# POST请求
url = 'http://127.0.0.1:9501/api/v1/public/login'
data = {'phone': '13585530888', 'password': '123456'}
# 中文可能会乱码
print(requests.post(url, data=data).text)
# 如果乱码,用content
print(requests.post(url, data=data).content.decode("utf-8"))

# POST RAW JSON 并返回 JSON
url = 'http://127.0.0.1:9501/api/v2/public/login'
data = {'phone': '13585530888', 'password': '123456'}
print(requests.post(url, json=data).json())

BeautifulSoup库

pip3 install bs4

pip3 install lxml

from bs4 import BeautifulSoup

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

soup = BeautifulSoup(html_doc, 'lxml')
html = soup.prettify()
print(html)

# 查找HTML文档的title,不推荐这样写:soup.title(因为没有代码提示了)
title = soup.find('title')
print(title.text)

# 查找title的父级元素
print(title.parent)

# 根据class查找所有的p标签
print(soup.findAll('p', attrs={'class': 'story'}))

# 查找所有的a链接地址
for a in soup.findAll('a'):
    print(a.get("href"))

# 提取所有的文字(去除标签)
print(soup.getText())

xpath

XML Path Language

pip3 install lxml

xpath的解析性能优于BeautifulSoup,但是使用起来没有BeautifulSoup方便

推荐使用,当爬取页面标签不规范(没有闭合等),xpath会进行自动修正

表达式 含义
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
| 或,两个节点的合计
text() 当前路径下的文本内容

xpath运算符

xpath函数

xpath轴


from lxml import etree

# 直接读取HTML文件
html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))

text = '''
<div>
    <ul>
         <li class="item-0" name='item-0'><a href="https://ask.hellobi.com/link1.html">first item</a></li>
         <li class="item-1"><a href="https://ask.hellobi.com/link2.html">second item</a></li>
         <li class="item-inactive"><a href="https://ask.hellobi.com/link3.html">third item</a></li>
         <li class="item-1"><a href="https://ask.hellobi.com/link4.html">fourth item</a></li>
         <li class="item-0 item-x"><a href="https://ask.hellobi.com/link5.html">fifth item</a>
     </ul>
 </div>
'''

html = etree.HTML(text)
# 示例代码中少了一个</li> tostring()方法可输出修正后的HTML代码,但是结果是bytes类型
result = etree.tostring(html)
print(result.decode('utf-8'))

# 选取所有节点
result = html.xpath('//*')
print(result)

# 选取所有li节点
result = html.xpath('//li')
print(result)

# 选取所有li节点下的直接子节点a
result = html.xpath('//li/a')
print(result)

# 选取所有li节点下的所有a节点
result = html.xpath('//ul//a')
print(result)

# 根据属性过滤,选取class=item-0的节点
result = html.xpath('//li[@class="item-0"]')
print(result)

# 获取href为...link4.html的a节点的父节点的class
result = html.xpath('//a[@href="https://ask.hellobi.com/link4.html"]/../@class')
print(result)

# 也可以通过parent::来获取href为...link4.html的a节点的父节点的class
result = html.xpath('//a[@href="https://ask.hellobi.com/link4.html"]/parent::*/@class')
print(result)

# 获取文本
result = html.xpath('//li[@class="item-0"]/a/text()')
print(result)

# 获取属性
result = html.xpath('//li/a/@href')
print(result)

# 属性多值匹配,有时候class会有多个值,直接用等于无法匹配,需要用到contains方法
# 注意contains是模糊匹配,contains(@class, "item")会匹配到所有的li
result = html.xpath('//li[contains(@class, "item-x")]/a/text()')
print(result)

# 多属性匹配
# and是运算符,xpath还有很多运算符
result = html.xpath('//li[contains(@class, "item-0") and @name="item-0"]/a/text()')
print(result)

# 返回匹配到的部分节点
# [1]匹配第一个节点,不是从0开始
result = html.xpath('//li[1]/a/text()')
print(result)
# last()匹配最后一个节点
result = html.xpath('//li[last()]/a/text()')
print(result)
# position()<3匹配位置需要小于3的,即位置1和2
result = html.xpath('//li[position()<3]/a/text()')
print(result)
# last()-2匹配最后1个节点往前推2个,即倒数第3个
result = html.xpath('//li[last()-2]/a/text()')
print(result)

print('xxx')
# ancestor轴,可以获取所有祖先节点,获取第一个li的所有祖先节点
result = html.xpath('//li[1]/ancestor::*')
print(result)
# 获取第一个li的div祖先节点
result = html.xpath('//li[1]/ancestor::div')
print(result)
# attribute轴,可以获取所有属性值
result = html.xpath('//li[1]/attribute::*')
print(result)
# child轴,可以获取所有直接子节点
result = html.xpath('//li[1]/child::a[@href="https://ask.hellobi.com/link1.html"]')
print(result)
#  descendant轴,可以获取所有子孙节点
result = html.xpath('//li[1]/descendant::span')
print(result)
# following轴,可以获取当前节点之后的所有节点,这里加了限制只获取了第二个后续节点
result = html.xpath('//li[1]/following::*[2]')
print(result)
# following-sibling轴,可以获取当前节点之后的所有同级节点
result = html.xpath('//li[1]/following-sibling::*')
print(result)

Selenium

chrome驱动

淘宝秒杀案例

获取医院信息

from selenium import webdriver
import time
import process_code

# 获取Chrome驱动
driver = webdriver.Chrome()
# 请求URL
driver.get("https://credit.jdzx.net.cn/portal/pubsearch/org/0114000000")
# 全屏
driver.fullscreen_window()
# 等待浏览器加载(最多等待10秒)
driver.implicitly_wait(10)
# 获取input输入框
name = driver.find_element_by_xpath('//*[@id="public-query"]/div[2]/div[2]/form/div[2]/input[1]')
# 输入内容
name.send_keys("上海市静安区老年医院")
# 获取验证码图片
code_img = driver.find_element_by_css_selector(
    '#public-query > div.right-box > div.bor-box > form > div:nth-child(4) > span.code-img > img')
# 截屏(只截验证码)
code_img.screenshot('./img/img.png')
# 等待1秒
time.sleep(1)
# 识别验证码
img_num = process_code.get_img_code('./img/img.png', 1001)
# 获取验证码输入框
code = driver.find_element_by_css_selector('#public-query > div.right-box > div.bor-box > form > div:nth-child(4) > input')
# 输入验证码
code.send_keys(str(img_num))
# 获取提交按钮
submit = driver.find_element_by_css_selector('#public-query > div.right-box > div.bor-box > form > div.btn-box > input')
# 点击提交
submit.click()
# 等待5秒
time.sleep(5)
# 获取响应数据
table = driver.find_element_by_xpath('//*[@id="formresult"]')
print(table.get_attribute("innerHTML"))
# 关闭浏览器
driver.quit()

其他常用方法

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select
import time

driver = webdriver.Chrome(executable_path='./chromedriver')

# 保存当前窗口
current_window = driver.current_window_handle

# 切换窗口
for handle in driver.window_handles:
    driver.switch_to.window(handle)
    if driver.title == 'xxx':
        break

# 切换回当前窗口
driver.switch_to.window(current_window)

# 切换到iframe
driver.switch_to.frame(driver.find_element_by_css_selector('xxx > xx > iframe'))

# 从iframe切换回来
driver.switch_to.default_content()

# 执行JavaScript
driver.execute_script('document.form.start_date.value="2021-01-01";')

# 获取元素属性
test = driver.find_element_by_css_selector("xxx")
# 获取id属性
test.get_attribute("id")
# 获取元素内部HTML
test.get_attribute("innerHTML")
# 获取元素内部HTML包含元素本身
test.get_attribute("outerHTML")
# 获取input的内容
test.get_attribute("value")
# 获取元素内容
test.get_attribute("innerText")

# 下拉框选择
dropdown = driver.find_element_by_css_selector('#dropdown')
Select(dropdown).select_by_value("1")

# 模拟特殊动作,如:双击、右键、移动、按住、移动到元素、移动到某个位置等
# 以移动到某个元素为例
action = ActionChains(driver)
action.move_to_element(driver.find_element_by_css_selector("xxx")).perform()

# 冻结页面,选择元素的时候的小技巧,在浏览器console中输入
# setTimeout(function(){debugger}, 5000)

# 弹出框相关
# 获取alert内容,点击alert
alert_text = driver.switch_to.alert.text
driver.switch_to.alert.accept()
# 获取confirm内容,确认或取消
confirm_text = driver.switch_to.alert.text
driver.switch_to.alert.accept()
driver.switch_to.alert.dismiss()
# prompt,获取内容,确认或取消和confirm一样,输入内容就send_keys就行了
driver.switch_to.alert.send_keys('我确定要删除这行内容')

# 三种等待方式
# 强制等待
time.sleep(1)
# 隐式等待,等待浏览器加载完毕,10秒超时
# 隐性等待对整个driver的周期都起作用,所以只要设置一次即可
driver.implicitly_wait(10)
# 显示等待,20秒超时,每秒检测
WebDriverWait(driver, 20, 1).until(EC.alert_is_present)
WebDriverWait(driver, 20, 1).until_not(driver.find_element_by_css_selector('xxx').is_selected())

读取XML

from lxml import etree

xml = '''
<class>
    <name>
        <bigname>张三</bigname>
        <smallname>阿三</smallname>
    </name>
    <test>%</test>
    <value>测试咯咯咯</value>
    <age>33</age>
    <sex>男</sex>
</class>
'''

# 读取字符串
# root = etree.fromstring(xml)

# 读取文件
tree = etree.parse('1.xml')
root = tree.getroot()

for element in root:
    # 查看所有属性或方法
    # print(dir(element))
    print(f"Element tag: {element.tag}, attribute: {element.get('attribute')}, text: {element.text}")
    if element.getchildren():
        # 示例数据只有2层,直接这样获取,多层的话要用递归
        for child in element.getchildren():
            print(f"Child tag: {child.tag}, attribute: {child.get('attribute')}, text: {child.text}")

Scrapy

TODO

results matching ""

    No results matching ""