一、爬虫基础知识
1.网络爬虫的主要类型
- 通用爬虫:从一些URL 扩充到整个WEB,主要为搜索引擎、大型WEB服务商采集数据;
- 聚焦爬虫:主要爬取特定主题的内容;
- 增量式爬虫:对已下载网页进行更新,只爬取页面更新的内容;
- 深层页面爬虫:指需要用户提交一些关键字才能获得的页面。
2.网页解析工具
- 正则表达式
- LXML库
- BS
3.python爬虫框架
- Scrapy
- Pyspider
- Cola
4.HTTP请求信息
请求头、请求方法、请求正文三部分组成
- 请求方法:POST/GET/DELETE/PUT/OPTIONS/TRACE/HEAD
- 请求头:一般反爬虫机制通过读取请求头来判断
二、Requests
1.基本属性
url:
params: (optional)
kwargs:
- data: (optional)
- json: (optional)
- headers: (optional)
- cookies: (optional)
- files: (optional) 上传文件
- auth: (optional)
- timeout: (optional) 超时等待时间
- allow_redirects: (optional) Boolean 开启、禁用重定向
- proxies: (optional) 设置代理
- verify: (optional)
- stream: (optional)
- cert: (optional) 支持的证书种类是pem,可用此参数转换证书
import requests
url = 'https://www.douban.com/search'
param = {
'q':'python',
'cat':1001,
}
res = requests.get(url=url,params=param)
#自动将param组成网址
#https://www.douban.com/search?q=python&cat=1001
res1 = requests.get(url=url,timeout=1)#自动设置timeout秒
print(res.text) #转码后的
print(res.content) #unicode 使用.decode('utf-8')解码
print(res.headers) #头部信息
print(res.url)
print(res.encoding) #网页编码方式
res1.history #Request对象的列表(如果出现301就是发生了重定向)
print(res.status_code) #200
print(res.reason) #OK
print(res.raw) #原始响应内容,返回一个requests对象
2、保持会话、代理
requests.session()
小工具
1.网址拼接工具
from urllib.parse import urlparse#网址拆分
from urllib.parse import urlunparse#网址拼接
urlparse("https://www.douban.com/search?cat=1001&q=python")
>>ParseResult(scheme='https', netloc='www.douban.com', path='/search', params='', query='cat=1001&q=python', fragment='')
urlunparse(['https','www.douban.com','search','',"q=python",''])
>>'https://www.douban.com/search?q=python'
2.lxml库解析网页
import requests
from lxml import etree
url = 'http://www.newsmth.net/nForum/#!article/HouseRent/552610'
data = requests.get(url=url)
selector = etree.HTML(data.text)
selector.xpath('//title/text()')
>>['水木社区-源于清华的高知社群']
3.xpath语法
语法 | 作用 |
---|---|
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
… | 选取当前节点的父节点。 |
@ | 选取属性。 |
bookstore | 选取 bookstore 元素的所有子节点。 |
/bookstore | 选取根元素 bookstore。 |
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素,不包含孙节点。 |
//book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 |
//@lang | 选取名为lang的所有属性。 |
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last( )] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last( )-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position( ) < 3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素 |
//title[@lang=‘eng’] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
/bookstore/book[price>35] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35 |
/bookstore/book[price>35]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35 |
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
node() | 匹配任何类型的节点。 |
//book/title l//book/price | 选取 book 元素的所有 title 和 price 元素。 |
//title l //price | 选取文档中的所有 title 和 price 元素。 |
/bookstore/book/title l //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。 |
4.Beautiful Soup语法
from bs4 import BeautifulSopu
5.正则表达式
import re
re语法 | 作用 |
---|---|
^ | 以……开头 |
$ | 以……结尾 |
. | 任意一个字符 |
* | 出现0–N次 |
( ) | 提取子串 |
? | 放在子串前匹配字符(非贪婪模式) |
“booobby” | ".*(b.*b). * "-----> ‘bb’ 贪婪模式,倒序匹配 |
“booobby” | ".*?(b.*b). * "-----> ‘booobb’ |
“booobby” | ".?(b.?b). * "-----> ‘booob’ |
+ | 出现1–N次 |
{m},{m,n},{m,} | 出现m次,出现m–n次,出现m次以上 |
[0-9],[ ^ 1 ] ,[135] | 0–9,非1数字,1或3或5 |
[.],[*] | 匹配. ,匹配* ,不再表示一个或多个字符 |
[a-z],[A-Z],[A-Z0-9] | a到z,A到Z,A到Z 或0到9 |
/s | 任意空白字符串 [\t\n\r\f] |
/S | 一个非空格就行 |
/w | a到z 或 A到Z 或0到9 或下划线 |
/W | 非(a到z 或 A到Z 或0到9),一些特殊符号 |
/d | 一个0到9 |
/D | 非一个0到9 |
[/u4E00-/u9FA5] | 大多数汉字 |
参考书
1.齐文光,《python网络爬虫实例教程》,人民邮电出版社:2018.08
三、Scrapy
1.Scrapy 各命令
bench Run quick benchmark test
#测试是否安装成功及每分钟发送多少个包
fetch Fetch a URL using the Scrapy downloader
#使用爬虫下载一个网页
#scrapy fetch "http://www.baidu.com"
genspider Generate new spider using pre-defined templates
#创建一个爬虫
runspider Run a self-contained spider (without creating a project)
#启动一个爬虫,无需创建整体项目
settings Get settings values
shell Interactive scraping console
#类似Ipython 环境,使用scrapy shell "http://www.baidu.com" ,直接返回response变量,主要来测试xpath是否正确
startproject Create new project
scrapy startproject <project_name> [project_dir]
#创建一个爬虫项目
version Print Scrapy version
#打印版本号
view Open URL in browser, as seen by Scrapy
#用浏览器的视图来查看页面
crawl Run a spider
#运行一个爬虫,必须在爬虫所在的目录中才能运行
2.Scrapy使用
1)创建爬虫项目
scrapy startproject Itcast
ITcast
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-36.pyc
│ │ ├── items.cpython-36.pyc
│ │ ├── pipelines.cpython-36.pyc
│ │ └── settings.cpython-36.pyc
│ ├── items.py
│ ├── middlewares.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-36.pyc
│ │ └── itcast.cpython-36.pyc
│ └── **itcast.py**
2)在ITcast项目下的一个爬虫itcast(在spiders文件下)
scrapy genspider itcast "www.itcast.cn"
3)执行itcast这个爬虫(在spiders文件下)
scrapy crawl itcast
4)itcast.py写法
不使用管道,直接把结果写入文件或直接打印出来
'''
以下内容是itcast.py文件中的
'''
import scrapy
from Demo.items import DemoItem #引入自定义的items
class ItcastSpider(scrapy.Spider): #继承Spider类
name = 'itcast' #genspider产生的
allowed_domains = ['http://www.itcast.cn'] #过滤作用,可删掉
start_urls = ['http://www.itcast.cn/channel/teacher.shtml']
def parse(self, response):
items = [] #可使用这种定义一个列表,最后把文件存储到单独文件里
datas = response.xpath("//div[@class='li_txt']")
for data in datas:
t_name = data.xpath("./h3/text()").extract()
t_title = data.xpath("./h4/text()").extract()
t_info = data.xpath("./p/text()").extract()
print(t_name)
item = DemoItem() #结构类似字典
item["t_name"] = t_name[0]
item["t_title"] = t_title[0]
item["t_info"] = t_info[0]
items.append(item)
print(items)
用管道来处理
把结果写到json文件中
'''
以下内容是itcast.py文件中的
'''
class ItcastSpider(scrapy.Spider):
name = 'itcast'
allowed_domains = ['http://www.itcast.cn']
start_urls = ['http://www.itcast.cn/channel/teacher.shtml']
def parse(self, response):
datas = response.xpath("//div[@class='li_txt']")
for data in datas:
t_name = data.xpath("./h3/text()").extract()
t_title = data.xpath("./h4/text()").extract()
t_info = data.xpath("./p/text()").extract()
print(t_name)
item = DemoItem()
item["t_name"] = t_name[0]
item["t_title"] = t_title[0]
item["t_info"] = t_info[0]
yield item #返回item,直接会由pipelines.py来处理
'''
以下内容是pipelines.py文件中的
需要打开setting中的 ITEM_PIPELINES
'''
import json
class DemoPipeline(object):
def __init__(self): #初始化时候打开文件
self.f = open('itcastTeacher.json','wb')
def process_item(self, item, spider): #item对应上面yield返回的item
content = json.dumps(dict(item),ensure_ascii=False) + ", \n"
self.f.write(content.encode('utf-8')) #encode将str转成byte,所以打开的时候用'wb'
return item #告诉调度器这条item处理完成,因此return不可少
def close_spider(self,spider):#结束爬虫的时候关闭文件
self.f.close()
把结果写到数据库中
注意事项
1.scrapy是多线程
2.只爬取了一条数据原因
一般由于xpath取的有问题
3.只爬取了第一页原因
DEBUG: Filtered offsite request to 'www.kuaidaili.com': <GET https://www.kuaidaili.com/free/inha/2/>
#由于在allow_domain中将第二页网址过滤掉了,因此有两种解决方案:
1. 在 allowed_domains 中加入 url 或将www前面的都删掉;
2. 在 scrapy.Request() 函数中将参数 dont_filter=True 设置为 True。
参照:Scrapy之Request函数回调未执行解决方案
4.json读取dumps存放的数据报异常
由于json读完数据将转换成字典,而字母key是唯一的,因此会报:json.decoder.JSONDecodeError: Extra data: