PythonCrawler 13day05

PythonCrawler 13day03

xpath的使用

代码全部在照哥的GIT

  1. 介绍

前面我们介绍了 BeautifulSoup 的用法,这个已经是非常强大的库了,不过还有一些比较流行的解析库,例如 lxml,使用的是 Xpath 语法,同样是效率比较高的解析方法。如果大家对 BeautifulSoup 使用不太习惯的话,可以尝试下 Xpath

  1. 安装
pip install lxml
  1. Xpath语法

Xpath是一门XML文档中查找信息的语言,XPath可以用来在XML文档中对元素和属性进行遍历。Xpath是W3C XSLT标准的主要元素。
具体语法参考W3C

  1. 节点的关系
  • 父(Parent)
    每个元素以及属性都有一个父。
    在下面的例子中,book 元素是 title、author、year 以及 price 元素的父:
<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>
  • 子(Children)

元素节点可有零个、一个或多个子。在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子:

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>
  • 同胞(Sibling)

拥有相同的父的节点,在下面的例子中,title、author、year 以及 price 元素都是同胞:

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>
  • 先辈(Ancestor)

某节点的父、父的父,等等。在下面的例子中,title 元素的先辈是 book 元素和 bookstore 元素:

<bookstore>
 
<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>
 
</bookstore>
  • 后代(Descendant)

某个节点的子,子的子,等等。
在下面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素:

<bookstore>
 
<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>
 
</bookstore>

选取节点
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
下面列出了最有用的路径表达式:

表达式描述
nodename选取此节点的所有子节点
/从根节点选取
//从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
.选取当前节点
选取当前节点的父节点
@选取属性

实例

在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径表达式结果
bookstore选取 bookstore 元素的所有子节点。
/bookstore选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book选取属于 bookstore 的子元素的所有 book 元素。
//book选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang选取名为 lang 的所有属性。

选取未知节点
XPath 通配符可用来选取未知的 XML 元素。

通配符描述
*匹配任何元素节点。
@*匹配任何属性节点。
node()匹配任何类型的节点。

实例
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
/bookstore/*选取 bookstore 元素的所有子元素。
//*选取文档中的所有元素。
//title[@*]选取所有带有属性的 title 元素。

选取若干路径

通过在路径表达式中使用“|”运算符,您可以选取若干个路径。

实例

路径表达式结果
//book/title//book/price
//title//price
/bookstore/book/title//price

lxml用法
初步使用

首先我们利用它来解析 HTML 代码,先来一个小例子来感受一下它的基本用法。

from lxml import etree
text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
</div>
'''
html = etree.HTML(text)
result = etree.tostring(html)
print(result)

首先我们使用 lxml 的 etree 库,然后利用 etree.HTML 初始化,然后我们将其打印出来。

其中,这里体现了 lxml 的一个非常实用的功能就是自动修正 html 代码,大家应该注意到了,最后一个 li 标签,其实我把尾标签删掉了,是不闭合的。不过,lxml 因为继承了 libxml2 的特性,具有自动修正 HTML 代码的功能。

所以输出结果是这样的:

<html><body>
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
 
</body></html>

不仅补全了 li 标签,还添加了 body,html 标签。

文件读取

除了直接读取字符串,还支持从文件读取内容。比如我们新建一个文件叫做 hello.html,内容为

<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html"><span class="bold">third item</span></a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a></li>
     </ul>
</div>

利用 parse 方法来读取文件:

from lxml import etree
html = etree.parse('hello.html')
result = etree.tostring(html, pretty_print=True)
print(result)

同样可以得到相同的结果。

XPath实例测试
依然以上一段程序为例

(1)获取所有的 < li> 标签

from lxml import etree
html = etree.parse('hello.html')
print type(html)
result = html.xpath('//li')
print result
print len(result)
print type(result)
print type(result[0])

运行结果:

<type 'lxml.etree._ElementTree'>
[<Element li at 0x1014e0e18>, <Element li at 0x1014e0ef0>, <Element li at 0x1014e0f38>, <Element li at 0x1014e0f80>, <Element li at 0x1014e0fc8>]
5
<type 'list'>
<type 'lxml.etree._Element'>

可见,etree.parse 的类型是 ElementTree,通过调用 xpath 以后,得到了一个列表,包含了 5 个 < li> 元素,每个元素都是 Element 类型

(2)获取 < li> 标签的所有 class

result = html.xpath('//li/@class')
print result

运行结果:

['item-0', 'item-1', 'item-inactive', 'item-1', 'item-0']

(3)获取 < li> 标签下 href 为 link1.html 的 < a> 标签

result = html.xpath('//li/a[@href="link1.html"]')
print result

运行结果

[<Element a at 0x10ffaae18>]

(4)获取 < li > 标签下的所有 < span> 标签

注意这么写是不对的

result = html.xpath('//li/span')

因为 / 是用来获取子元素的,而 < span> 并不是 < li> 的子元素,所以,要用双斜杠

正确代码为:

result = html.xpath('//li//span')
print result

运行结果

[<Element span at 0x10d698e18>]

(5)获取 < li> 标签下的所有 class,不包括 < li>

result = html.xpath('//li/a//@class')
print result

运行结果

['blod']

(6)获取最后一个 < li> 的 < a> 的 href

result = html.xpath('//li[last()]/a/@href')
print result

运行结果

['link5.html']

(7)获取倒数第二个元素的内容

 result = html.xpath('//li[last()-1]/a')
print result[0].text

运行结果:

fourth item

(8)获取 class 为 bold 的标签名

result = html.xpath('//*[@class="bold"]')
print result[0].tag

运行结果

span

通过以上实例的练习,相信大家对 XPath 的基本用法有了基本的了解。也可以利用 text 方法来获取元素的内容。

pyquery的使用

我的电脑网速不好,没办法下载具体参考下面
使用方法
以后会具体出一个Pyquery模块的博文

jsonpath的使用

数据提取之JSON与JsonPATH
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它是的人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景,比如网站前台与后台之间的数据交互。

JSON和XML的比较可谓不相上下。

Python2.7中自带了JSON模块,直接import json就可以使用了。
官方博客:http://docs.python.org/library/json.html

JSON
json简单说就是javascripy 中的对象和数组,所以这两种结构就是对象和数组两种结构,通过这两种结构可以表示各种复杂的结构

  1. 字典

对象在js中表示为{}括起来的内容,数据结构为{key:value,key:value…}的键值对的结构,在面向对象的语言中,key为对象属性,value为对应的属性值,所以很容易理解,取值方法为对象(注意这是个点–》).key获取属性值,这个属性值的类型可以是数字,字符串、数组、对象这几种。

  1. 列表(数组)

数组在js中是中括号[]括起来的内容,数据结构为[“Python”,“javascript”,“c++”,…],取值方式
和所有语言一样,使用索引获取,字段值的类型可以是数字,字符串,数组,对象几种

  1. json模块

json模块提供了四个功能:dumps、dump、loads、load,用于字符串和python数据类型间进行转换

json.loads()把Json格式字符串解码转换成Python对象

把Json格式字符串解码转换成Python对象,从json到python 的类型转化对照如下:

JSONPython
objectdict
arraylist
stringunicode
number(int)int,long
number(real)float
trueTrue
falseFalse
nullNone

从json到python的转化关系实例

import json


strList = '[1, 2, 3, 4]'

strDict = '{"city":"北京", "name":"大猫"}'

json.loads(strList)
for str in strList:
    print(str)
#[1, 2, 3, 4]

json.loads(strDict)  #json数据自动按Unicode存储
#{u'city': u'\u5317\u4eac', u'name': u'\u5927\u732b'

json.dumps()–转化成字符串的操作

实现python类型转化为json字符串,返回一个str对象 把一个Python对象编码转换成Json字符串。
从python原始类型向json类型的转化对照如下:

PythonJSON
dictobject
list,tuplearray
str,unicodestring
int,long,floatnumber
Truetrue
Falsefalse
Nonenull

json.dumps()序列化时默认使用的是ascii编码,添加参数ensure_ascii=False,禁用ascii
编码,按utf-8编码
python向json转化实例:

#json_dumps.py

#-*- coding:utf-8 -*-

import json
import chardet

listStr = [1, 2, 3, 4]
tupleStr = (1, 2, 3, 4)
dictStr  = {"city":"北京", "name":"大猫"}

print(type(json.dumps(listStr)))
# '[1, 2, 3, 4]'

print(type(json.dumps(tupleStr)))
# '[1, 2, 3, 4]'

#注意,json.dumps()序列化时默认使用ascii编码
#添加参数 ensure_ascii = False,禁用ascii编码,按utf-8编码
#chardet.detect()返回字典,其中confidence是检测精确度。

print(json.dumps(dictStr))
#'{"city":"\\u5317\\u4eac", "name":"\\u5927\\u5218"}'

print(chardet.detect(json.dumps(dictStr)))

print(json.dumps(dictStr, ensure_ascii=False))

print(chardet.detect(json.dumps(dictStr, ensure_ascii=False)))

json.dump()
将Python内置类型序列化为json对象后写入文件

#json_dump.py

import json

listStr = [{"city":"北京"}, {"name":"大刘"}]

json.dump(listStr, open("listStr.json", "w"), ensure_ascii=False)

dictStr = {"city":"北京", "name":"大刘"}
json.dump(dictStr.open("dictStr.json", "w"), ensure_ascii=False)

json.load()
读取文件中json形式的字符串元素转化成python类型

#-*- coding:utf-8 -*-


import json

strList = json.load(open("listStr.json"))
print strList
# [{u'city': u'\u5317\u4eac'}, {u'name': u'\u5927\u5218'}]

strDict = json.load(open("dictStr.json"))
print strDict
# {u'city': u'\u5317\u4eac', u'name': u'\u5927\u5218'}

综合实例:
jsonpath使用方法:
json:

import json

"""
dump    把json字符串写入文件
load    读取文件中的json对象
dumps   把python对象转换为json字符串
loads   把json字符串转换为python对象
"""

da_lao_list = {
    "stars": {
        "No1": "高虎",
        "No2": "房祖名",
        "No3": "张默",
        "No4": "陈羽凡",
        "No5": "宁财神",
        "No6": "宋冬野"
    }
}

# filepath
#dict>>>str (Python对象>>>json字符串)
result_dumps = json.dumps(da_lao_list)
print(result_dumps)
print(type(result_dumps))

#str>>>dict
result_loads = json.loads(result_dumps)
print(result_loads)
print(type(result_loads))


#读出python对象
json.dump(result_loads, open("dump.txt", mode="w", encoding="utf-8"))

#写入pythonb对象
result_load = json.load(open("dump.txt"))
print(result_load)
print(type(result_load))

dump 把python对象写入文件
load 读取文件中的python对象
dumps 把json对象转换为json字符串
loads 把json字符串转换为json对象

注意:这里的python对象即为字典

jsonpath:
具体用法:

import requests
import jsonpath
import json

url = 'http://www.lagou.com/lbs/getAllCitySearchLabels.json'
response = requests.get(url)
html_str = response.content.decode()

# 把json格式字符串转换成python对象
jsonobj = json.loads(html_str)

# 从根节点开始,匹配name节点
citylist = jsonpath.jsonpath(jsonobj, '$..name')

fp = open('city.json', 'w')
#将python对象转为json字符串
content = json.dumps(citylist, ensure_ascii=False)
#然后将字符串转为二进制写入文件
fp.write(content.encode('utf-8'))
fp.close()
JSONPathResult
$.store.book[*].authorstore中的所有的book的作者
$…author所有的作者
$.store.*store下的所有的元素
$.store…pricestore中的所有的内容的价格
$…book[2]第三本书
$…book[(@.length-1)]$…book[-1:]
$…book[0,1]$…book[:2]
$…book[?(@.isbn)]获取有isbn的所有数
$…book[?(@.price<10)]获取价格大于10的所有的书
$…*获取所有的数据
{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

多线程的使用

我们之前写的爬虫都是单个线程的?这怎么够?一旦一个地方卡到不动了,那不就永远等待下去了?为此我们可以使用多线程或者多进程来处理。

爬虫使用多线程来处理网络请求,使用线程来处理URL队列中的url,然后将url返回的结果保存在另一个队列中,其它线程在读取这个队列中的数据,然后写到文件中去

主要组成部分

URL队列和结果队列:
将将要爬去的url放在一个队列中,这里使用标准库Queue。访问url后的结果保存在结果队列中

初始化一个URL队列

from queue import Queue
urls_queue = Queue()
out_queue = Queue()

请求线程

使用多个线程,不停的取URL队列中的url,并进行处理:

import threading

class ThreadCrawl(threading.Thread):
    def __init__(self, queue, out_queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.out_queue = out_queue

    def run(self):
        while True:
            item = self.queue.get()

如果队列为空,线程就会被阻塞,直到队列不为空。处理队列中的一条数据后,就需要通知队列已经处理完该条数据.

处理线程

处理结果队列中的数据,并保存到文件中。如果使用多个线程的话,必须要给文件加上锁

lock = threading.Lock()
f = codecs.open('out.txt', 'w', 'utf8')

当线程需要写入文件的时候,可以这样处理:

with lock:
    f.write(something)

Queue模块中的常用方法:

Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步

  • Queue.qsize() 返回队列的大小

  • Queue.empty() 如果队列为空,返回True,反之False

  • Queue.full() 如果队列满了,返回True,反之False Queue.full 与 maxsize 大小对应

  • Queue.get([block[, timeout]])获取队列,timeout等待时间 Queue.get_nowait()

  • 相当Queue.get(False) Queue.put(item) 写入队列,timeout等待时间

  • Queue.put_nowait(item) 相当Queue.put(item, False) Queue.task_done()

  • 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号 Queue.join( )

  • 实际上意味着等到队列为空,再执行别的操作

多线程爬取糗事百科案例代码:

import requests
from lxml import etree
from queue import Queue
import threading
 
class QiubaiSpider:
    def __init__(self):
        self.url_temp = "https://www.qiushibaike.com/text/page/{}/"
        self.headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"}
        self.url_queue = Queue()
        self.html_queue = Queue()
        self.content_queue = Queue()
 
    def get_url_list(self):
        for i in range(1,4):
            self.url_queue.put(self.url_temp.format(i))
 
    def parse_url(self):
       
        while True:
            url = self.url_queue.get()
 
            print(url)
            response = requests.get(url,headers=self.headers)
            self.html_queue.put(response.content.decode())
            self.url_queue.task_done()
 
 
    def get_content_list(self):
 
        while True:
 
            html_str = self.html_queue.get()
            html = etree.HTML(html_str)
            div_list = html.xpath("//div[@id='content-left']/div")
            content_list = []
            for div in div_list:
                item = {}
                item["id"] = div.xpath(".//h2/text()")
                item["id"] = [i.replace("\n", "") for i in item["id"]]
                item["content"] = div.xpath(".//div[@class='content']/span/text()")
                item["content"] = [i.replace("\n", "") for i in item["content"]]
                item["author_image"] = div.xpath(".//div/a/img/@src")
                item["author_image"] = "https:"+item["author_image"][0] if len(item["author_image"]) > 0 else None
                item["age"] = div.xpath(".//div[contains(@class, 'articleGender')]/text()")
                item["age"] = item["age"] if len(item["age"]) > 0 else None
                item["author_gender"] = div.xpath(".//div[contains(@class, 'articleGender')]/@class")
                item["author_gender"] = item["author_gender"][0].split(" ")[-1].replace("Icon", "") if len(item["author_gender"]) > 0 else None
                item["stats_vote"] = div.xpath(".//div/span/i/text()")
                item["stats_vote"] = item["stats_vote"][0] if len(item["stats_vote"]) > 0 else None
                item["user_commons"] = div.xpath(".//span[@class='stats-comments']/a/i/text()")
                item["user_commons"] = item["user_commons"][0] if len(item["stats_vote"]) > 0 else None
                content_list.append(item)
            # return content_list
            self.content_queue.put(content_list)
            self.html_queue.task_done()
 
 
    # 打印内容
    def save_content_list(self):
 
        while True:
            content_list = self.content_queue.get()
            for i in content_list:
                # print(i)
                pass
            self.content_queue.task_done()
 
    def run(self):
        thread_list = []
        # 1.url_list
        t_url = threading.Thread(target=self.get_url_list)
        thread_list.append(t_url)
        # 2.遍历,发送请求,获取响应
        for i in range(20):
            t_parse = threading.Thread(target=self.parse_url)
            thread_list.append(t_parse)
        # 3.提取数据
        for i in range(2):
            t_html = threading.Thread(target=self.get_content_list)
            thread_list.append(t_html)
        # 4.保存
        t_save = threading.Thread(target=self.save_content_list)
        thread_list.append(t_save)
        for t in thread_list:
            t.setDaemon(True)  # 把子线程设置为守护线程,该线程不重要主线程结束,子线程结束
            t.start()
 
        for q in [self.url_queue, self.html_queue, self.content_queue]:
            q.join()  # 让主线程等待阻塞,等待队列的任务完成之后再完成
 
        print("主线程结束")
if __name__ == '__main__':
    qiubaiSpider = QiubaiSpider()
    qiubaiSpider.run()
 

输出结果为:
在这里插入图片描述

tesseract的使用

这个太高深了,还在研究中,以后会出一篇专门的博客写tesseract
这个博主写的很好大家看一下厉害的人写的哦

好多天才写出来惭愧,,,加油呀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值