python-spider个人笔记

python 之禅:

Beautiful is better than ugly.(美丽优于丑陋)
Explicit is better than implicit.(直白优于含蓄)
Simple is better than complex.(简单优于复杂)
Complex is better than complicated.(复杂优于繁琐)
Readability counts.(可读性很重要)

1.通用性爬虫(搜索引擎)

pagerank:

网页搜索排名–nofollow算法可以抵制一些垃圾投票

以图搜图
听歌识曲
robots协议(道德层面),保存在根目录下:https://www.taobao.com/robots.txt

2.聚焦爬虫

  • HTTP:

    超文本传输协议,默认端口80

    2016年提出HTTP/2.0 版本新增并发请求

  • HTTPS(http + ssl):

    http + ssl(安全套接字层),默认端口443

    相对于http更安全,但是性能较低

页面数据获取途径:

  • 当前url相应中
  • ajax异步请求相应中
  • js生成

相应状态码:

  • 1xx:浏览器发送的请求不完整
  • 2xx:请求正常完成
  • 3xx:
    • 302, 307:重定向
    • 304:资源未发生变更,直接使用本地缓存
  • 4xx:请求的资源地址有误,服务器无法提供相应
    • 404:请求路径不存在
    • 403:拒绝访问(权限)
  • 5xx:服务器相应过程中出现错误
    • 500:服务器内部错误

字符

字符集:多个字符的集合(ASCII, GB2312, GB18030, Unicode)

ASCII 编码是1个字节,Unicode编码是2个字节,UTF-8是Unicode边长的编码方式(可以是1, 2, 3个字节)

r = requests.get(url):

r.encoding(‘utf-8’)

r.text (默认由headers进行推测,获得解码后str类型数据)

常用方法:

    r.content  # 获得bytes类型数据
    r.status_code
    r.request.headers
    r.headers

命令行修改:

    ~/.bashrc
    添加:
    alias fanyi='python             /User/linlin/Desktop/fanyi.py'

    source ~/.bashrc

代理:

为什么使用代理?

  • 让服务器以为不是同一个客户端在请求
  • 防止我们的真实地址被泄露,防止被追究

nginx反向代理

保持登陆状态

1.携带登录后cookie至headers中

2.在请求方法中添加cookies参数(字典)

3.创建session实例

    se = requests.session()
    se.post()  # 登陆,获得登陆成功cookie
    se.get()  # 自动携带cookie发送请求

js定位:

  • 选择会出发js事件的按钮,点击event listener
  • search all file(request_url或者其中关键字)

获取响应中的cookie

    response.cookies  # 获取cookie对象
    requests.utils.dict_from_cookiejar(response.cookies)  # 获得cookie字典
    requests.cookiejar_from_dict({key:value})  # 将字典转化为cookie对象

url地址编解码:

    requests.utils.unquote()  # 解码
    requests.utils.quote()  # 编码

SSL证书验证跳过

requests.get(url, verify=False)

请求超时验证

requests.get(url,timeout=10) # 超时时间10秒

第三方包安装

  • pip install package_name
  • python setup.py install
  • pip install *.whl

返回响应中不是标准json字符串

callback = jsonp1参数可以省略

格式化输出

pprint() # pretty print

格式化写入

f.write(json.dumps(str, ensure_ascii = False, indent = 4)

类文件对象

具有read() 或者wrtie() 的对象就是类文件对象

可以使用json.load()json.dump() 转换

正则表达式

  • . 匹配除\n之外的所有
  • \d 数字
  • \D 非数字
  • \s 空白字符(空格),包含\r\t\n\f\v
  • \S 非空白
  • \w 单词[A-Za-z0-9_]
  • \W 非单词字符
  • * 任意次数
  • + 1次或无限次
  • ? 非贪婪
  • {m} m次数

re.VERBOSE(或re.X) 可使正则表达式结构化,形式更易读.

re.DOTALL(或re.S) 使得. 匹配包括换行符在内的任意字符.

re.IGNORECASE(或re.I) 使得匹配对大小写不敏感.

re.MULTILINE(或re.M) 使得多行匹配生效,影响^$ 的首尾匹配

re.compile()提前进行编译,例:r = re.compile('\d');p.findall(str) # 需要添加re.S等参数时,需要提前在编译时添加

Xpath

  • 获取文本 /html/title/text()
  • 获取属性 /html/link/@href
  • 获取列表 /html/a
  • 当前节点 ./
  • 上一级节点 ../
  • 获取列表中第一个 /html/a[1]
  • 取列表中最后一个 /html/a[last()]
  • 取前两个/html/a[position()<3]
  • 或者/html/a[1]|/html/a[3] //a[1]|//[3]
  • 当前节点中某个位置标签 /html//a
  • 选择id或者class固定的标签 /html/a[@id="id"] /html/a[@class="class"]
  • 获取当前标签下所有标签的文本 /html/a//text()
  • 根据文本筛选 //a[text() = '下一页']

lxml

  • 导入lxml的etree库
    • lxml会自动修改html代码
  • html = etree.HTML(text) # text 可以是str或者bytes,获得html对象
  • html.xpath()
  • etree.tostring(html) # 转换为字符串,提前查看修正后代码
  • 包含(class包含i的divpyth: //div[contains(@classs, "i")]

多线程

    import threading
    from queue import Queue

    url_queue = Queue()
    url_queue.put()  
    url_queue.get()
    url_queue.task_done()  # 标志当前取出完成,任务队列-1 

    t_list = list()
    t1 = threading.Thread(target=xxx)
    t_list.append(t1)
    for i in range(3):  # 创建3个线程实例
        t2 = threading.Thread(target=aaaa)
        t_list.append(t2)
    for i in t_list:
        t.setDaemon(True)  # 设置主线程为守护线程(该线程不重要,主线程结束,子线程立即结束)
        i.start() 
    for q in queue_list: # queue_list 为当前所有子线程队列列表
        q.join()  # 使主线程等待

selenium&phantomJS

安装:
注意事项:

selenium 首页选择会在页面加载完成后执行,后续页面会直接执行,所以,需要的请求后续页面后强制睡眠几秒钟,常用time.sleep(3) ;也可以使用 1.显式等待WebDriverWait(driver, 10), until(EC.presence_of_element_located((By.ID,"myDynamicElement")) 直到myDyamicElement的id出现 2. 隐式等待

    driver.implicitly_wait(10)  # 等待10s,默认0
    driver.get("http://www.xxxxx.com/loading")
    myDynamicElement = driver.find_element_by_id("myDynamicElement")
使用
    from selenium import webdriver
    driver = webdirver.Chrome()  # 实例化浏览器
    driver.set_window_size(1920, 1080)  # 设置窗口大小
    driver.maximizez-window()  # 最大化窗口
    driver.get(url)  
    driver.save_screenshot('xxx.png') # 保存浏览器截图
    driver.find_element_by_id() 返回对象
    driver.find_elements_by_class() # 返回对象列表
    driver.find_element_by_id('kw').send_kdy('搜索关键字')
    drvier.find_element_by_id('su').click()  # 点击选中对象
    driver.find_element_by_class_name()  # 根据类名来筛选,类名只能写一个!!! 
    driver.page_source # 获取渲染后页面代码
    driver.get_cookies() # 获取当前页面cookie
    driver.find_element_by_di('kw').send_key(Keys.CONTROL, 'a')  # ctrl +a 全选输入框内容
    driver.find_element_by_id('kw').send_key(Keys.RETURN)  # 模拟点击Enter回车键
    driver.find_element_by_id('kw').clear()  # 清除输入框内容
    driver.current_url # 当前请求的url

    driver.close() # 关闭当前页面
    driver.quit() # 退出浏览器

云打码

用来识别验证码

鼠标动作链

    #导入 ActionChains 类
from selenium.webdriver import ActionChains

# 鼠标移动到 ac 位置
ac = driver.find_element_by_xpath('element')
ActionChains(driver).move_to_element(ac).perform()


# 在 ac 位置单击
ac = driver.find_element_by_xpath("elementA")
ActionChains(driver).move_to_element(ac).click(ac).perform()

# 在 ac 位置双击
ac = driver.find_element_by_xpath("elementB")
ActionChains(driver).move_to_element(ac).double_click(ac).perform()

# 在 ac 位置右击
ac = driver.find_element_by_xpath("elementC")
ActionChains(driver).move_to_element(ac).context_click(ac).perform()

# 在 ac 位置左键单击hold住
ac = driver.find_element_by_xpath('elementF')
ActionChains(driver).move_to_element(ac).click_and_hold(ac).perform()

# 将 ac1 拖拽到 ac2 位置
ac1 = driver.find_element_by_xpath('elementD')
ac2 = driver.find_element_by_xpath('elementE')
ActionChains(driver).drag_and_drop(ac1, ac2).perform()

填充下拉框

# 导入 Select 类
from selenium.webdriver.support.ui import Select

# 找到 name 的选项卡
select = Select(driver.find_element_by_name('status'))

# 
select.select_by_index(1)  # index 索引从 0 开始
select.select_by_value("0")  # value是option标签的一个属性值,并不是显示在下拉框中的值
select.select_by_visible_text(u"未审核")  # visible_text是在option标签文本的值,是显示在下拉框的值

# 全部取消
select.deselect_all()

弹窗处理

alert = driver.switch_to_alert()

页面切换

# 第一种
driver.switch_to.window("this is window name")

# 第二种
for handle in driver.window_handles:
    driver.switch_to_window(handle)

页面前进和后退

driver.forward()     #前进
driver.back()        # 后退

mongodb

备份: (终端下)

mongodump -h dbhost -d db_name -o save_route

恢复:

mongorestore -h dbhost -d db_name –dir dir_route

聚合:

  • db.collection.aggregate({管道:{表达式}})
    • $match
    • $group
    • $project
    • $sort
    • $limit
    • $skip
    • $unwind
  • db.orders.aggregate([{$match:{status:'A'}},{$group:{_id:"$cust_id", total:{$sum:1}}}])
    • $match 获得的值交给$group继续处理;$sum:1表示以1为倍数求和

索引:

​ db.集合.ensureIndex({属性:1},{‘unique’:true}) —设置唯一,可以去重(配合布隆过滤器)

​ db.集合.getIndexes() —查询索引

​ db.集合.dropIndex({})

查询时间:

​ db.stu.find({name:‘“test10000”’}).explain(‘executionStats’)

MySQL & MongoDB:

  • 创建数据库

    • create database 数据库名 character set UTF8
    • use 数据库名
  • 查看数据库

    • show databases;
    • show dbs show databases
  • 删除数据库

    • drop database数据库名
    • db.dropDatabase() –在使用当前db状态下
  • 创建表或者集合

    • create table 表明 (字段名 类型 约束)
      • create table user(name varchar(20) unique, id integer primary key auto _increment, img varchar(20));
    • db.集合名.insert({})
      • db.createCollection(集合名, {参数})
  • 删除表

    • drop table 表名
    • db.集合名.drop()
  • MySQL修改表

    • alter table 表名 add/modify/drop/change 字段名 数据类型 约束
      • alter table user add age integer;
      • alter table user name varchar(50);
      • alter table user drop img;
      • alter table user change name username varchar(70);
    • rename table旧表名 to 新表名; – 重命名表名
  • 数据增删改

    • 插入
      • insert into 表名(字段名) values (值)
        • inser into 表名 values(所有字段内容)
      • db.集合名.insert({})
        • db.集合名.save({}) 存在则更新,不存在则新增
    • 修改
      • update 表名 set 字段名=值, 字段名=值 where 条件
      • db.集合名.update({条件}, {改值}, {multi:true/false})
        • db.集合名.update({条件},{$set:{修改}}) 更改当前字段
    • 删除
      • delete from 表名 where 条件
        • truncate table 表名 —-回滚id
      • db.集合名.remove({条件],{justOne:true/false})
    • 查询
      • select * from 表名 where 条件
      • db.集合名.find({条件})
      • db.集合名.find({条件}).pretty()
      • 条件
        • 比较
          • age > 18
          • {$gt:{age:18}}
        • 逻辑
          • where age > 18 and age < 30
          • {age:{$gte:18}, age:{$lt:30}}
          • where age >20 or age < 18
          • {$or:[{age:{$gt:20}, {age:{$lt:18}}}]}
          • where not age > 18
          • {$not:{age:{$gt:18}}}
        • 范围
          • where age in [18, 18, 20]
          • where age between 18 and 20
          • {age:{$in:[18, 20]}}
        • 正则/模糊
          • where name like “张%”
          • where name like “张_”
          • {name:/^张/}
          • {name:{$regex:"^张"}}
        • 自定义查询
          • {$where:function(){return this.name=="张三"}}
        • 分页
          • select * from 表名 where 条件 limit x,y ;—x从那条开始,y查询几条
          • db.集合名,find({条件}}).skip(x).limit(y)
        • 去重
          • select distinct 字段名 from 表名 where 条件;
          • db.集合名.distinct(“去重字段”, {条件})
        • 投影
          • select 字段名, 字段名 from 表名 where 条件;
          • db.集合名.find({条件}, {_id: 0, 字段,:1}) — 0不显示, 1 显示
        • 排序
          • select * from 表名 where 条件 order by 字段 desc/asc;
          • db.集合名.find({条件}).sort({字段:-1, 字段:1}) —1升序,-1降序
        • 统计
          • select count(*) from 表名 where 条件
          • db.集合名.count({条件})
          • db.集合名.find({条件}).count()
  • MySQL高级查询[可以省略的参数]

    • SELECT select_expr [,select|_expr,...] [      
            FROM tb_name
            [WHERE 条件判断]
            [GROUP BY {col_name | postion} [ASC | DESC], ...] 
            [HAVING WHERE 条件判断]
            [ORDER BY {col_name|expr|postion} [ASC | DESC], ...]
            [ LIMIT {[offset,]rowcount | row_count OFFSET offset}]
      ]
    • 关联查询

      • 交叉(cross join)(选取两个表中所有排列组合)
        • select * from 表A, 表B
        • select * from 表A, [cross] join 表B
      • 内链接(inner join)
        • select * from 表A, 表B where 关联条件(a.id= b.id)
        • select * from 表A [inner] join 表B on 关联条件
      • 外链接(outer join)
        • 左(left [outer] join
          • 在内连接的基础上,把左表中没有关联上的记录不上,右表使用null填充数据
          • select * from 表A left [outer] join 表B on 关联条件
        • 右(right [outer] join
          • select * from 表A right[outer] join 表B on 关联条件
    • 子查询(查询嵌套:一个查询作为另一个查询的基础)

      • 可以放在select/where/from等后边

pymongo

from pymongo import MongoClient
client = MongoClient(host="", port="27017")
collection = client['db_name']['collections_name']
collection.insert({})
collection.insert_many([{},{},{}])
t = collection.find()  # 返回游标对象,可以遍历一次,也可以进行强制类型转换为list()
collection.update_one()
collection.update_many()
collection.delete_one()
collection.delete_many()

# 获得id可以被100整除的列
ret = collection.find()
data = list(ret)
data = [i for i in data if i["_id"]%100 == 0]

回调函数,事件驱动

回调函数:创建但不执行的函数

事件驱动:靠事件来驱动程序的执行的编写代码的方式

  • 三要素
    • 事件
    • 事件源
    • 事件处理程序(监听器,监听程序)
  • 三个步骤
    • 事件源
    • 编写事件处理程序
    • 绑定(οnclick=)

scrapy

python-spider个人笔记_第1张图片

日志:

import logging  # 使用python自带日志功能
logging.basicConfig(level=logging.WARNING,  
                    filename='./log/log.txt',  
                    filemode='w',  
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') 
logger = logging.getLogger(__name__)  简单输出当前文件名
logger.warning(e)

scrapy.Request:

yield scrapy.Request(
    url,
    [
        callback, method="GET", headers, body, cookies, meta, dont_filter=False
    ]
)

请求数据重复

请求传递item为同一个,多线程操作item会造成数据覆盖,可以使用deepcopy来修正

crawl spider

scrapy genspider -t crawl spider_name allow_domain

参数:

多个rules提取的url之间不能传递参数

allow:正则,先提取,后不提取(会自动补全url)

callback:处理响应的函数(不能定义parse函数,避免覆盖父类方法)

follow:请求后资源是否继续被筛选

deny:满足正则的url不被请求

process_links:指定该spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。

process_request:指定该spider中哪个的函数将会被调用, 该规则提取到每个request时都会调用该函数。 (用来过滤request)

python-spider个人笔记_第2张图片

downloadmiddleware:

添加代理ip:

reqeust.meta[‘proxy’] = ‘http://127.0.0.1:455’

pycharm发布代码

tools – deployment – sftp

Crontab 定时执行

安装:sudo apt-get install cron

配置:crontab -e

​ 分 小时 日 月 星期 命令

​ 0-59 0-23 1-31 1-12 0-6 command

查看:crontab -l

tail -f 1.log有输出就会展示

星期0表示周日

你可能感兴趣的:(spider)