爬虫基础知识:

爬虫程序小测试(Java版)

爬虫就是获取网页并提取和保存信息的自动化程序

ch1:正则表达式的构建
 re库中的常用方法:match、search、findall、sub方法
 Python文件的常用操作
 CSV文件的写入

match()方法:从字符串的开头开始匹配的,一旦开头不匹配,那么整个匹配就失败,更适合用来检测某个字符串是否符合某个正则表达式的规则
search()方法:依次扫描字符串,直到找到第一个符合规则的字符串,然后返回匹配内容,否则失败。主要用于查询一次性结果
findall()方法:依次扫描字符串,直到找到所有符合规则的字符串列表,然后返回匹配内容,否则失败。主要用于查询所有结果

re库:

# 爬虫就是获取网页并提取和保存信息的自动化程序

# ch1:
# 一、正则表达式的构建
# 二、
#  re库中的常用方法:match、search、findall、sub方法
# match()方法:从字符串的开头开始匹配的,一旦开头不匹配,那么整个匹配就失败,更适合用来检测某个字符串是否符合某个正则表达式的规则
# search()方法:依次扫描字符串,直到找到第一个符合规则的字符串,然后返回匹配内容,否则失败。主要用于查询一次性结果
# findall()方法:依次扫描字符串,直到找到所有符合规则的字符串列表,然后返回匹配内容,否则失败。主要用于查询所有结果
# search()与findall()可以搭配group()方法:
# 如果想从字符串中提取一部分内容可以使用()括号将待提取的子字符串括起来。()实际上标记了一个子表达式的开始和结束位置,
# 被标记的每个子表达式会依次对应每一个分组,调用group()方法传入分组的索引即可获取提取的结果。

# re.sub(repl, string, content, count)
# 当 repl 是字符串,则使用 repl 去替换字符串每一个匹配的子串并返回替换后的字符串;若repl 是函数,这个方法应当只接受一个参数并返回一个字符串用于替换
# string表示原来的字符串,
# count 用于指定最多替换次数,不指定时全部替换。
# 例:
# content = '2005-2018 douban.com, all rights reserved'
# result = re.sub('\d{4}', 'hello welcome', content, 1)
# print(result)
# 结果:hello welcome-2018 douban.com, all rights reserved

# 三、
#  Python文件的常用操作
# 相对路径:
# with open('./data/test.txt') as f:
# 四、
#  CSV文件的写入

# import csv
#
# headers = ['name', 'age']
# datas = [{'name': 'Bob', 'age': 23},
# {'name': 'Jerry', 'age': 44},
# {'name': 'Tom', 'age': 15}]
# 也可写成绝对路径
# with open('example.csv', 'w', newline='') as f:
# writer = csv.DictWriter(f, headers)
# writer.writeheader()
# for row in datas:
# writer.writerow(row) # writer.writerows(datas)

# ch2:
import re

import requests
# BeautifulSoup借助网页的结构和属性等特性来解析网页。BeautifulSoup类《==》标签树
from bs4 import BeautifulSoup

# 请求在线网址:
# 二进制输出图片
# url='https://gss0.bdstatic.com/5bVWsj_p_tVS5dKfpU_Y_D3/res/r/image/2019-09-26/a64fec2c10cfffd46f24eb793692971b.png'
# response = requests.get(url)
# # .split('/')[-1]用于分割字符串,取倒数第一段被/分割的部分,[]可以为1,-1,-2等
# pic_name = url.split('/')[-2]+'.'+url.split('.')[-1]
# # 按二进制形式输出
# with open(pic_name, 'wb+') as f:
# f.write(response.content)

BeautifulSoup库:

# r = requests.get('http://python123.io/ws/demo.html')
# demo = r.text
# soup = BeautifulSoup(demo, 'lxml')
# print(soup)

# 一、节点选择器:
# 孩子和子孙节点的遍历(向下遍历)
# for child in soup.body.descendants:
# for child in soup.body.children:
# print(child)

# 所有父节点的遍历(向上遍历)
# for parent in soup.a.parents:
# if parent is None:
# print(parent)
# else:
# print(parent.name)

# 所有兄弟节点的前/后继(平行遍历)(或者说层次遍历)
# for sibling in soup.a.previous_siblings:
# print(sibling)
# for sibling in soup.a.next_siblings:
# print(sibling)

# 单个后继
# print(soup.a.next_sibling)
# # 后继的后继
# print(soup.a.next_sibling.next_sibling)
# # 后继的前驱(即本身)
# print(soup.a.next_sibling.previous_sibling)

# 二、方法选择器:
# tag_name.string属性适用于内含一个标签节点,输出值类型bs4.element.NavigableString
# tag_name.txt属性适用于内含多标签的节点,输出值为str
# < >.find_all(name, attrs, recursive, string, **kwargs)
# 返回一个列表类型,存储查找的结果。
# • name : 对标签名称的检索字符串。
# • attrs: 对标签属性值的检索字符串,可标注属性检索
# • recursive: 是否对子孙全部检索,默认True
# • string: <>…</>中字符串区域的检索字符串

# ps = soup.find_all(string=re.compile("python"))
# print(ps)
# for p in ps:
# print(p.string)
# for p in ps:
# print(p.text)

# 三、CSS选择器:
# element,element div,p 选择所有 <div> 元素和所有 <p> 元素。
# element element div p 选择 <div> 元素内部的所有 <p> 元素。
# element>element div>p 选择父元素为 <div> 元素的所有 <p> 元素。
# element+element div+p 选择紧接在 <div> 元素之后的所有 <p> 元素。
# print(soup.select('p,a'))
# print(soup.select('p a'))

# 打开本地网页:
# soup2 = BeautifulSoup(open('1.html',encoding='utf-8'), 'lxml')
# print(soup2.prettify())

Selenium库:

# 因为 requests 获取的都是原始的 HTML 文档,而浏览器中的页面则是经过JavaScript 处理数据后生成的结果,这些数据的来源有多种,可能是通过 Ajax 加载的,
# 可能是包含在 HTML 文档中的,也可能是经过 JavaScript 和特定算法计算后生成的 。

# 为了解决这些问题,我们可以直接使用模拟浏览器运行的方式来实现,这样就可以做到在浏览器中看到是什么样,抓取的源码就是什么样,也就是可见即可爬。
# 这样我们就不用再去管网页内部的JavaScript用了什么算法渲染页面,不用管页面后台的Ajax接口到底有哪些参数。

# Selenium 是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见即可爬。
# 对于一些 JavaScript 动态渲染的页面来说,此种抓取方式非常有效。

# http://npm.taobao.org/mirrors/chromedriver/
# 建议直接将 chromedriver.exe 保存至 Python 的 Scripts 目录下

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 示例:打开一个网址并输出其源码和cookie等内容,指定元素类型进行查找并获取属性等:
browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
print(browser.page_source)
print(browser.get_cookies)
# id属性可以获取节点id,location属性可以获取该节点在页面中的相对位置,tag_name属性可以获取标签名称,size属性可以获取节点的大小,也就是宽高。
# 还可以获取text属性,每个WebElement节点都有text属性,直接调用这个属性就可以得到节点内部的文本信息,这相当于Beautiful Soup的get_text()方法。
# logo = browser.find_element(By.ID, 'J_SiteNav')
# print(logo)
# print(logo.get_attribute('class')) # get_attribute()方法来获取节点的属性
# print(logo.text)
#
# input = browser.find_element(By.ID, 'q')
# input.send_keys('iPhone')
# time.sleep(1)
# input.clear()
# input.send_keys('iPad')
# button = browser.find_element(By.CLASS_NAME, 'btn-search')
# button.click()
#
# browser.get('https://www.zhihu.com/explore')
# browser.execute_script('window.scrollTo(0, document.body.scrollHeight)') # 滑到底部
# browser.execute_script('alert("To Bottom")') # 使浏览器弹出提示信息(需手动点击关闭)
# time.sleep(3)

# 浏览器的前进后退操作
# browser.back()
# time.sleep(1)
# browser.forward()

# 新开启一个选项卡并切换:
# browser.execute_script('window.open()')
# print(browser.window_handles)
# browser.switch_to.window(browser.window_handles[1])
# browser.get('https://www.taobao.com')
# time.sleep(1)
# browser.switch_to.window(browser.window_handles[0])


# cookie操作(获取、添加、删除Cookies等):
# browser.get('https://www.zhihu.com/explore')
# print(browser.get_cookies())
# browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'germey'})
# print(browser.get_cookies())
# browser.delete_all_cookies()
# print(browser.get_cookies())

# 切换Frame:
# url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
# browser.get(url)
# browser.switch_to.frame('iframeResult')
# try:
# logo = browser.find_element(By.CLASS_NAME, 'logo')
# except NoSuchElementException:
# print('NO LOGO')
# 切换到父Frame:
# browser.switch_to.parent_frame()
# logo = browser.find_element(By.CLASS_NAME, 'logo')
# print(logo)
# print(logo.text)

# 延时等待:
# 等待的方式有两种:1.隐式等待(全局):browser.implicitly_wait(10) 2.显式等待
# 3.强制等待time.sleep(t)表进程挂起

# 显式等待(等待条件--expected_conditions):
# WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions=None).until(method,message=‘’)
# driver: webdriver的驱动程序,如(IE、FireFox、chrome、safari等)
# timeout:超时时间,默认以秒为单位
# poll_frequency: 休眠时间(步长)的间隔,默认为0.5秒,即检测元素是否存在的频率
# ignored_exception: 超时后的异常信息,默认情况下抛 “NoSuchElementException”
# until(method,message=”)调用该方法提供的驱动程序做为一个参数,直到返回值不为False 

# 这里首先引入WebDriverWait这个对象,指定最长等待时间,然后调用它的until()方法,传入要等待条件expected_conditions。
# 比如,这里传入了presence_of_element_located这个条件,代表节点出现的意思,其参数是节点的定位元组,也就是ID为q的节点搜索框。
# 这样可以做到的效果就是,在10秒内如果ID为q的节点(即搜索框)成功加载出来,就返回该节点;如果超过10秒还没有加载出来,就抛出异常。
# 对于按钮,可以更改一下等待条件,比如改为element_to_be_clickable,也就是可点击,所以查找按钮时查找CSS选择器为.btn-search的按钮,
# 如果10秒内它是可点击的,也就是成功加载出来了,就返回这个按钮节点;如果超过10秒还不可点击,也就是没有加载出来,就抛出异常。

wait = WebDriverWait(browser, 10)
input = wait.until(EC.presence_of_element_located((By.ID, 'q')))
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search')))
print(input, button)

# 补充:等待条件及其含义:
# title_is 标题是某内容
# title_contains 标题包含某内容
# presence_of_element_located 节点加载出来,传入定位元组,如(By.ID, 'p')
# visibility_of_element_located 节点可见,传入定位元组
# visibility_of 可见,传入节点对象
# presence_of_all_elements_located 所有节点加载出来
# text_to_be_present_in_element 某个节点文本包含某文字
# text_to_be_present_in_element_value 某个节点值包含某文字
# frame_to_be_available_and_switch_to_it 加载并切换
# invisibility_of_element_located 节点不可见
# element_to_be_clickable 节点可点击
# staleness_of 判断一个节点是否仍在DOM,可判断页面是否已经刷新
# element_to_be_selected 节点可选择,传节点对象
# element_located_to_be_selected 节点可选择,传入定位元组
# element_selection_state_to_be 传入节点对象以及状态,相等返回True,否则返回False
# element_located_selection_state_to_be 传入定位元组以及状态,相等返回True,否则返回False
# alert_is_present 是否出现警告

browser.close()

scrapy框架:

item.py:

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy
from scrapy import item


class TutorialItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass


class QuoteItem(scrapy.Item):
    # 提前定义好QuoteItem的每一项元素名称用来存放网页中的对应元素
    # define the fields for your item here like:
    # name = scrapy.Field()
    text = scrapy.Field()
    author = scrapy.Field()
    tags = scrapy.Field()

quote.py:

import scrapy

from tutorial.items import QuoteItem

# scrapy框架介绍(具体流程和配置信息参考流程与配置图):
#  Engine引擎,用来处理整个系统的数据流处理,触发事务,是整个框架的核心。
#  Scheduler调度器,用来接受引擎发过来的请求并加入队列中,并在引擎再次请求的时候提供给引擎。
#  Downloader下载器,用于下载网页内容,并将网页内容返回给蜘蛛。
#  Spiders蜘蛛,其内定义了爬取的逻辑和网页的解析规则,它主要负责解析响应并生成提取结果和新的请求
#  Item Pipeline项目管道,负责处理由蜘蛛从网页中抽取的项目,它的主要任务是清洗、验证和存储数据。

# 项目目录介绍:
#  scrapy.cfg:部署爬虫项目的配置文件。
#  __init__.py:爬虫项目的初始化文件,用来对项目做初始化工作。
#  items.py:爬虫项目的数据容器文件,用来定义要获取的数据。 # 列出item中每一个数据项,在爬取时填充item的数据项返回item数据元素组成json的每一项或者CSV的每一行
#  middlewares.py:爬虫项目的中间件文件。
#  pipelines.py:爬虫项目的管道文件,用来对items中的数据进行进一步的加工处理。
#  settings.py:爬虫项目的设置文件,包含了爬虫项目的设置信息。
#  __pycache__/:缓存目录,无需修改
# spiders目录下:
# __init__.py:初始文件,无需修改
# __pycache__:缓存目录,无需修改
# quotes: spider的实现

# Scrapy爬虫框架的使用流程:
# 创建一个Scrapy爬虫工程:scrapy stratproject <工程名字>
# 例:cd进入要创建的文件位置,scrapy startproject tutorial
# 在工程中产生一个Scrapy爬虫:scrapy genspider <爬虫名字> <网站域名>
# 例:cd tutorial #进入项目tutorial,scrapy genspider quotes quotes.toscrape.com,即scrapy框架会自动配置好项目的部分初始信息
# 配置产生的spider爬虫,获取、解析网页内容(主要有items.py和quotes.py等)
# 运行爬虫以获取网页:scrapy crawl <爬虫名字>或者将爬虫结果保存至指定文件:scrapy crawl <爬虫名字> -o <文件名.格式>

# scrapy命令补充:
# startproject 创建一个新工程 scrapy startproject <name> [ dir]
# genspider 创建一个爬虫 scrapy genspider [options] <name> <domain>
# settings 获得爬虫配置信息 scrapy settings [options]
# crawl 运行一个爬虫 scrapy crawl <spider>
# list 列出工程中所有爬虫 scrapy list
# shell 启动URL调试命令行 scrapy shell [url]


# QuotesSpider爬虫类大体框架示意:
#  name,爬虫名字,以区分不同的spider
#  allowed_domains:允许爬虫的域名
#  start_urls :spider启动时爬取的列表
#  Parse:解析的方法


class QuotesSpider(scrapy.Spider):
    name = 'quotes'
    # http://quotes.toscrape.com/page/1/ 是要爬取的网站(入口地址)(队列首部网站)
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/']

    # Selector是一个可以独立使用的模块,可以直接利用Selector这个类来构建一个选择器对象,然后调用它的相关方法如xpath()、css()等来提取数据
    # from scrapy import Selector
    # 同BeautifulSoup等库类似,Selector其实也是强大的网页解析库。如果方便的话,我们也可以在其他项目中直接使用Selector来提取数据。
    # scrapy的选择器同时还对接了CSS选择器,使用response.css()方法可以使用CSS选择器来选择对应的元素。
    # from scrapy import Selector
    # body = '<html><head><title>Hello World</title></head><body></body></html>'
    # selector = Selector(text=body)
    # title = selector.xpath('//title/text()').extract_first()
    # print(title)

    def parse(self, response):

        # 利用response.css()方法使用CSS选择器来选择对应元素:
        quotes = response.css('.quote')
        for quote in quotes:
            # 每一个对应元素存到对应的item中(QuoteItem类型)(对应元素赋对应名字的网页元素)
            # 这里的每个item单独列为json的一项或者CSV的一行
            item = QuoteItem()
            text = quote.css('.text::text').extract_first()
            author = quote.css(".author::text").extract_first()
            tags = quote.css(".tag::text").extract()
            item['text'] = text
            item['tags'] = tags
            item['author'] = author
            yield item

        # 下面一段不应在for循环内,如果在的话也没事,scrapy框架有自动去重功能
        # 利用回调函数callback继续添加后续网址到队列中
        next_page = response.css('.next a::attr(href)').extract_first()
        url = response.urljoin(next_page)
        # scrapy.Request函数:url和callback参数:
        yield scrapy.Request(url=url, callback=self.parse)

# CSS选择器补充(Selector):
#  response.css('a'):返回的是selector对象;
#  response.css('a').extract():返回的是a标签对象;
#  response.css('a::text').extract_first():返回的是第一个a标签中文本的值;
#  response.css('a::attr(href)').extract_first():返回的是第一个a标签中href属性的值;
#  response.css('a[href*=image]::attr(href)').extract():返回所有a标签中href属性包含image的值;
#  response.css('a[href*=image] img::attr(src)').extract():返回所有a标签下image标签的src属性;
#
#  类选择器:元素的class属性,比如.intro表示选择 class="intro" 的所有元素
#  ID选择器:元素的id属性,比如#firstname表示选择 id="firstname" 的所有元素
#  元素选择器:直接选择文档元素,比如p表示选择所有的p元素,div表示选择所有的div元素
#  属性选择器:选择具有某个属性的元素,如*[title]表示选择所有包含title属性的元素、a[href]表示选择所有带有href属性的a元素等
#  后代选择器:选择包含元素后代的元素,如li a表示选取所有li 下所有a元素
#  子元素选择器:选择作为某元素子元素的元素,如h1 > strong表示选择父元素为h1 的所有 strong 元素
#  相邻兄弟选择器:选择紧接在另一元素后的元素,且二者有相同父元素,如h1 + p表示选择紧接在h1 元素之后的所有p元素