### 本周学习任务 ``` 1. selenium 模拟爬取数据及自动化测试(重点) 2. scrapy爬虫 3. 反爬虫技术(重点) 4. 广度比深度重要,扩展知识面. ``` ### js混淆 使用反混淆得到反混淆的js代码 把js代码替换成python代码 使用正则, xpath, css解析html文件 ### CSS选择器 - CSS 指层叠样式表 (**C**ascading **S**tyle **S**heets) - 样式定义**如何显示** HTML 元素 - 样式通常存储在**样式表**中 - 把样式添加到 HTML 4.0 中,是为了**解决内容与表现分离的问题** - **外部样式表**可以极大提高工作效率 - 外部样式表通常存储在 **CSS 文件**中 - 多个样式定义可**层叠**为一 ### css:after 选择器 :after 选择器向选定的元素之后插入内容。 使用[content](http://www.runoob.com/cssref/pr-gen-content.html) 属性来指定要插入的内容。 ```css <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>菜鸟教程(runoob.com)</title> <style> p:after { content:"- Remember this"; background-color:yellow; color:red; font-weight:bold; } </style> </head> <body> <p>My name is Donald</p> <p>I live in Ducksburg</p> <p><b>注意:</b>:after作用于IE8 以及更早版本的浏览器,DOCTYPE 必须已经声明.</p> </body> </html> ``` 结果: ```css My name is Donald- Remember this I live in Ducksburg- Remember this 注意::after作用于IE8 以及更早版本的浏览器,DOCTYPE 必须已经声明.- Remember this ``` ### css:before 选择器 before 选择器向选定的元素前插入内容。 使用[content](http://www.runoob.com/cssref/pr-gen-content.html) 属性来指定要插入的内容。 ```css <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>菜鸟教程(runoob.com)</title> <style> p:before { content:"Read this -"; background-color:yellow; color:red; font-weight:bold; } </style> </head> <body> <p>My name is Donald</p> <p>I live in Ducksburg</p> <p><b>注意:</b> :before 作用于 IE8, DOCTYPE 必须已经声明.</p> </body> </html> ``` 结果: ```css Read this -My name is Donald Read this -I live in Ducksburg Read this -注意: :before 作用于 IE8, DOCTYPE 必须已经声明. ``` ### nth-child() 选择器 nth-child(n) 选择器匹配父元素中的第 n 个子元素,元素类型没有限制。 *n* 可以是一个数字,一个关键字,或者一个公式。 ```css <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>菜鸟教程(runoob.com)</title> <style> p:nth-child(odd) { background:#ff0000; } p:nth-child(even) { background:#0000ff; } </style> </head> <body> <h1>This is a heading</h1> <p>The first paragraph.</p> <p>The second paragraph.</p> <p>The third paragraph.</p> <p><b>注意:</b> Internet Explorer 8 and以及更早版本的浏览器 :nth-child()选择器.</p> </body> </html> ``` 结果: ### 爬虫三大块 1. 抓取网页(requests(urllib), scrapy, cookie, ajax,字体加密) 2. 解析(网页结构复杂) 3. 存储(监控变化:md5, sha等信息摘要) ### 多线程 从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算机任务. 一个CPU,在一个时间切片里只能运行一个程序. 从操作系统的角度: 进程和线程,都是一种CPU的执行单元. 进程:表示一个程序的上下文执行活动,(打开, 执行, 保存). 线程:进程执行程序时候的最小调度到位(执行a, 执行b...). 一个程序至少有一个进程, 一个进程至少有一个线程. 多进程/多线程: 表示可以同时执行多个任务,进程和线程的调度是由操作系统自动完成. 进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享. 进程之间的通信有操作系统传递,导致通讯效率低,切换开销大. ### 互斥锁 线程: 一个进程可以有多个线程,所有先后线程共享进程得内存空间,通讯效率高,切换开销小. 共享意味着竞争,导致数据不安全,为了保护内存空间的数据安全,引入"互斥锁". 一个线程在访问内存空间的时候,其他线程不允许访问,必须等待之前的线程访问结束,才能使用这个内存空间. 互斥锁: 一种安全有序的让多个线程访问内存空间的机制. ### GIL GIL 全局解释器锁: 线程的执行权限,在Python的进程只有一个GIL. 一个线程需要执行任务,必须获取GIL. 好处:直接杜绝了多个线程访问内存空间的安全问题. 坏处: Python的多线程不是真的多线程,不能充分利用多核CPU的资源.但是在I/O阻塞的时候,解释器会释放GIL. ### IO IO在计算机中指Input/Output,也就是输入和输出。 由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。 第一种是CPU等着,也就是程序暂停执行后续代码,等100M的数据在10秒后写入磁盘,再接着往下执行,这种模式称为同步IO; 另一种方法是CPU不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,于是,后续代码可以立刻接着执行,这种模式称为异步IO。 同步和异步的区别就在于是否等待IO执行的结果。好比你去麦当劳点餐,你说“来个汉堡”,服务员告诉你,对不起,汉堡要现做,需要等5分钟,于是你站在收银台前面等了5分钟,拿到汉堡再去逛商场,这是同步IO。 ## selenium(自动化测试) ### 必写代码 ```python from selenium import webdriver from selenium.webdriver.firefox.options import Options options = Options() browser = webdriver.Firefox(executable_path='F:/mo_ni_gong_ju_pa_chong/geckodriver.exe', firefox_options=options) ``` ### 常用语法 模拟打开一个网页 ```python browser.get('http://www.baidu.com') ``` 关闭浏览器 ```python browser.close() ``` 最大化窗口 - ```python browser.maximize_window() ``` 查找需要内容 - ```python browser.find_element_by_xpath browser.find_element_by_id # a标签里的text文件才能用此函数 article = browser.find_element_by_link_text('Log In') # 等等等等 ``` 点击 - ```python login = browser.find_element_by_xpath('//button') 点击login login.click() ``` 输入 - ```python content = browser.find_element_by_id('content') content.send_keys('老子就是想请假') ``` ###模拟鼠标放上(ActionChains:动作链) - ```python from selenium import webdriver from selenium.webdriver.firefox.options import Options as FOptions from selenium.webdriver.common.action_chains import ActionChains options = FOptions() browser = webdriver.Firefox(executable_path="F:/mo_ni_gong_ju_pa_chong/geckodriver.exe", firefox_options=options) browser.get('http://example.webscraping.com') # a标签里的text文件才能用此函数 article = browser.find_element_by_link_text('Log In') # 产生一个移动鼠标的动作链,移动到Log In元素(article) perform 执行前面的动作链 ActionChains(browser).move_to_element(article).perform() menu = browser.find_element_by_xpath('//a[@href="/places/default/user/register?_next=/places/default/index"]') menu.click() ``` 打印网页源码 - ```python browser.page_source ``` 执行javescript语句 - ```python browser.get("www.baidu.com") time.sleep(3) # 滚动到页面底部 browser.execute_script("window.scrollTo(0,document.body.scrollHeight)") ``` 切换到新窗口 - ```python browser.get("http://www.baidu.com") handles = browser.window_handles browser.switch_to.window(handles[-1]) ``` 打印弹窗中的内容 - ```python t = browser.switch_to_alert() print(t.text) ``` ## 元素拖拽 - drag_and_drop()方法使用,drag_and_drop()方法涉及到参数传递,一个是要拖拽元素的起点,一个是要拖拽元素的终点 - ```python browser.get("http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable") browser.switch_to.frame('iframeResult') # 起点 source = browser.find_element_by_id('draggable') # 终点 target = browser.find_element_by_id('droppable') actions = ActionChains(browser) actions.drag_and_drop(source,target) # 执行以上动作链 actions.perform() ``` ### <frame> 标签定义 frameset 中的一个特定的窗口(框架) ```python browser.switch_to.frame('iframeResult') ``` # Scrapy(爬虫) ### 安装scrapy ```python pip install scrapy ``` windows下报错没有c++包,需要到https://www.lfd.uci.edu/~gohlke/pythonlibs/ 下载跟电脑相对应的版本Twisted. twisted是一个异步网络框架. 创建scrapy爬虫项目 ```python scrapy startproject genera_scrapy # CountrySpider 项目名 ```  创建爬虫文件 ```python scrapy genspider CountrySpider example.com # CountrySpider 爬虫文件名) ```  配置settings文件 ```python ROBOTSTXT_OBEY = False # 把默认Ture 改为False # ITEM_PIPELINES 释放开 ITEM_PIPELINES = { 'genera_scrapy.pipelines.GeneraScrapyPipeline': 300, } ``` 简单的爬虫代码 ```python import scrapy class CountryspiderSpider(scrapy.Spider): name = 'CountrySpider' allowed_domains = ['webscraping.com'] start_urls = ['http://example.webscraping.com/places/default/view/China-47/'] def parse(self, response): print(response.body) item = {} item['name'] = response.css('tr#places_country__rowtd.w2p_fw::text').extract_first() item['population'] = response.css('tr#places_population__rowtd.w2p_fw::text').extract_first() yield item ``` 存入sqlite3数据库 ```python import sqlite3 class GeneraScrapyPipeline(object): # 系统要求的处理item的函数, 不能随意写 def process_item(self, item, spider): print(item) conn = sqlite3.connect("country.db") cursor = conn.cursor() save_dic = {"name": item['name'], "population": item['population']} sql_insert = """insert into country values(?,?)""" param = (item['name'], item['population']) cursor.execute(sql_insert, param) conn.commit() return item ``` ### sqlite3 sql语句操作 下载安装sqlite,添加到环境变量中 ### 插入数据 - 插入一条数据 ```python INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN ); ``` - 插入多条数据 ``` INSERT INTO table_name (field1, field2,...fieldN) VALUES (valueA1,valueA2,...valueAN),(valueB1,valueB2,...valueBN),(valueC1,valueC2,...valueCN)......; ``` ### 查询数据 - 以下为在MySQL数据库中查询数据通用的 SELECT 语法: ``` SELECT column_name,column_name FROM table_name [WHERE Clause] [LIMIT N][ OFFSET M] ``` - 以下实例将返回数据表 runoob_tbl 的所有记录 ``` select * from runoob_tbl; ``` #### where 条件查询 ``` SELECT field1, field2,...fieldN FROM table_name1, table_name2... [WHERE condition1 [AND [OR]] condition2..... ``` - 以下实例将读取 runoob_tbl 表中 runoob_author 字段值为 Sanjay 的所有记录: ``` SELECT * from runoob_tbl WHERE runoob_author='菜鸟教程'; ``` - MySQL 的 WHERE 子句的字符串比较是不区分大小写的。 你可以使用 BINARY 关键字来设定 WHERE 子句的字符串比较是区分大小写的。 ``` mysql> SELECT * from runoob_tbl WHERE BINARY runoob_author='RUNOOB.COM'; ``` ### 更新数据 - 以下是 UPDATE 命令修改 MySQL 数据表数据的通用 SQL 语法: ``` UPDATE table_name SET field1=new-value1, field2=new-value2 [WHERE Clause] ``` - 将 id 为 5 的手机号改为默认的 - ```python update students settel=default where id=5; ``` - 将所有人的年龄增加 1 ``` update students set age=age+1; ``` - 将手机号为 13288097888 的姓名改为 "小明", 年龄改为 19: ``` update students setname="小明", age=19 where tel="13288097888"; ``` - 以下实例将更新 runoob_id 为 3 的runoob_title 字段值的 "C++" 替换为 "Python": ``` UPDATE runoob_tbl SET runoob_title = REPLACE(runoob_title, 'C++', 'Python') where runoob_id = 3; ``` ### 删除数据 - 以下是 SQL DELETE 语句从 MySQL 数据表中删除数据的通用语法: ``` DELETE FROM table_name [WHERE Clause] ``` - delete 语句用于删除表中的数据, 基本用法为: > - 删除 id 为 3 的行: delete from students where id=3; > - 删除所有年龄小于 21 岁的数据: delete from students where age<20; > - 删除表中的所有数据: delete from students; - delete,drop,truncate 都有删除表的作用,区别在于: > - 1、delete 和 truncate 仅仅删除表数据,drop 连表数据和表结构一起删除,打个比方,delete 是单杀,truncate 是团灭,drop 是把电脑摔了。 > - 2、delete 是 DML 语句,操作完以后如果没有不想提交事务还可以回滚,truncate 和 drop 是 DDL 语句,操作完马上生效,不能回滚,打个比方,delete 是发微信说分手,后悔还可以撤回,truncate 和 drop 是直接扇耳光说滚,不能反悔。 > - 3、执行的速度上,**drop>truncate>delete**,打个比方,drop 是神舟火箭,truncate 是和谐号动车,delete 是自行车。 ### 模糊查询 - like 匹配/模糊匹配,会与 % 和 _ 结合使用 。 ```python '%a' //以a结尾的数据 'a%' //以a开头的数据 '%a%' //含有a的数据 '_a_' //三位且中间字母是a的 '_a' //两位且结尾字母是a的 'a_' //两位且开头字母是a的 ``` - 查询以 java 字段开头的信息。 > ``` > SELECT * FROM position WHERE name LIKE 'java%'; > ``` - 查询包含 java 字段的信息。 > ``` > SELECT * FROM position WHERE name LIKE '%java%'; > ``` - 查询以 java 字段结尾的信息。 > ``` > SELECT * FROM position WHERE name LIKE '%java'; > ``` ### 连接两个以上的 SELECT 语句的结果组合到一个结果集合中 - **UNION 语句**:用于将不同表中相同列中查询的数据展示出来;(不包括重复数据) - **UNION ALL 语句**:用于将不同表中相同列中查询的数据展示出来;(包括重复数据)使用形式如下: ```python SELECT 列名称 FROM 表名称 UNION SELECT 列名称 FROM 表名称 ORDER BY 列名称; SELECT 列名称 FROM 表名称 UNION ALL SELECT 列名称 FROM 表名称 ORDER BY 列名称; ``` ### 排序 (默认升序, ASC升序, DESC降序) - SELECT * from runoob_tbl ORDER BY submission_date ASC; - SELECT * from runoob_tbl ORDER BY submission_date DESC;