Skip to content

Commit 9385d43

Browse files
committed
更新了部分代码
1 parent 8e861a8 commit 9385d43

File tree

6 files changed

+221
-4
lines changed

6 files changed

+221
-4
lines changed

Day66-75/04.并发下载.md

+143
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,145 @@
11
## 并发下载
22

3+
### 多线程和多进程回顾
4+
5+
6+
7+
### 实例 - 多线程下载“手机搜狐网”所有页面。
8+
9+
```Python
10+
11+
from enum import Enum, unique
12+
from queue import Queue
13+
from random import random
14+
from threading import Thread, current_thread
15+
from time import sleep
16+
from urllib.parse import urlparse
17+
18+
import requests
19+
from bs4 import BeautifulSoup
20+
21+
22+
@unique
23+
class SpiderStatus(Enum):
24+
IDLE = 0
25+
WORKING = 1
26+
27+
28+
def decode_page(page_bytes, charsets=('utf-8',)):
29+
page_html = None
30+
for charset in charsets:
31+
try:
32+
page_html = page_bytes.decode(charset)
33+
break
34+
except UnicodeDecodeError:
35+
pass
36+
return page_html
37+
38+
39+
class Retry(object):
40+
41+
def __init__(self, *, retry_times=3,
42+
wait_secs=5, errors=(Exception, )):
43+
self.retry_times = retry_times
44+
self.wait_secs = wait_secs
45+
self.errors = errors
46+
47+
def __call__(self, fn):
48+
49+
def wrapper(*args, **kwargs):
50+
for _ in range(self.retry_times):
51+
try:
52+
return fn(*args, **kwargs)
53+
except self.errors as e:
54+
print(e)
55+
sleep((random() + 1) * self.wait_secs)
56+
return None
57+
58+
return wrapper
59+
60+
61+
class Spider(object):
62+
63+
def __init__(self):
64+
self.status = SpiderStatus.IDLE
65+
66+
@Retry()
67+
def fetch(self, current_url, *, charsets=('utf-8', ),
68+
user_agent=None, proxies=None):
69+
thread_name = current_thread().name
70+
print(f'[{thread_name}]: {current_url}')
71+
headers = {'user-agent': user_agent} if user_agent else {}
72+
resp = requests.get(current_url,
73+
headers=headers, proxies=proxies)
74+
return decode_page(resp.content, charsets) \
75+
if resp.status_code == 200 else None
76+
77+
def parse(self, html_page, *, domain='m.sohu.com'):
78+
soup = BeautifulSoup(html_page, 'lxml')
79+
url_links = []
80+
for a_tag in soup.body.select('a[href]'):
81+
parser = urlparse(a_tag.attrs['href'])
82+
scheme = parser.scheme or 'http'
83+
netloc = parser.netloc or domain
84+
if scheme != 'javascript' and netloc == domain:
85+
path = parser.path
86+
query = '?' + parser.query if parser.query else ''
87+
full_url = f'{scheme}://{netloc}{path}{query}'
88+
if full_url not in visited_urls:
89+
url_links.append(full_url)
90+
return url_links
91+
92+
def extract(self, html_page):
93+
pass
94+
95+
def store(self, data_dict):
96+
pass
97+
98+
99+
class SpiderThread(Thread):
100+
101+
def __init__(self, name, spider, tasks_queue):
102+
super().__init__(name=name, daemon=True)
103+
self.spider = spider
104+
self.tasks_queue = tasks_queue
105+
106+
def run(self):
107+
while True:
108+
current_url = self.tasks_queue.get()
109+
visited_urls.add(current_url)
110+
self.spider.status = SpiderStatus.WORKING
111+
html_page = self.spider.fetch(current_url)
112+
if html_page not in [None, '']:
113+
url_links = self.spider.parse(html_page)
114+
for url_link in url_links:
115+
self.tasks_queue.put(url_link)
116+
self.spider.status = SpiderStatus.IDLE
117+
118+
119+
def is_any_alive(spider_threads):
120+
return any([spider_thread.spider.status == SpiderStatus.WORKING
121+
for spider_thread in spider_threads])
122+
123+
124+
visited_urls = set()
125+
126+
127+
def main():
128+
task_queue = Queue()
129+
task_queue.put('http://m.sohu.com/')
130+
spider_threads = [SpiderThread('thread-%d' % i, Spider(), task_queue)
131+
for i in range(10)]
132+
for spider_thread in spider_threads:
133+
spider_thread.start()
134+
135+
while not task_queue.empty() or is_any_alive(spider_threads):
136+
sleep(5)
137+
138+
print('Over!')
139+
140+
141+
if __name__ == '__main__':
142+
main()
143+
144+
```
145+

Day66-75/07.数据清洗.md

-2
This file was deleted.

Day66-75/Scrapy的应用01.md

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
## Scrapy的应用(01)
2+
3+
### Scrapy概述
4+
5+
Scrapy是Python开发的一个非常流行的网络爬虫框架,可以用来抓取Web站点并从页面中提取结构化的数据,被广泛的用于数据挖掘、数据监测和自动化测试等领域。下图展示了Scrapy的基本架构,其中包含了主要组件和系统的数据处理流程(图中的绿色箭头)。
6+
7+
![](./res/scrapy-architecture.jpg)
8+
9+
#### 组件
10+
11+
1. Scrapy引擎(Engine):Scrapy引擎是用来控制整个系统的数据处理流程。
12+
2. 调度器(Scheduler):调度器从Scrapy引擎接受请求并排序列入队列,并在Scrapy引擎发出请求后返还给它们。
13+
3. 下载器(Downloader):下载器的主要职责是抓取网页并将网页内容返还给蜘蛛(Spiders)。
14+
4. 蜘蛛(Spiders):蜘蛛是有Scrapy用户自定义的用来解析网页并抓取特定URL返回的内容的类,每个蜘蛛都能处理一个域名或一组域名,简单的说就是用来定义特定网站的抓取和解析规则。
15+
5. 条目管道(Item Pipeline):条目管道的主要责任是负责处理有蜘蛛从网页中抽取的数据条目,它的主要任务是清理、验证和存储数据。当页面被蜘蛛解析后,将被发送到条目管道,并经过几个特定的次序处理数据。每个条目管道组件都是一个Python类,它们获取了数据条目并执行对数据条目进行处理的方法,同时还需要确定是否需要在条目管道中继续执行下一步或是直接丢弃掉不处理。条目管道通常执行的任务有:清理HTML数据、验证解析到的数据(检查条目是否包含必要的字段)、检查是不是重复数据(如果重复就丢弃)、将解析到的数据存储到数据库(关系型数据库或NoSQL数据库)中。
16+
6. 中间件(Middlewares):中间件是介于Scrapy引擎和其他组件之间的一个钩子框架,主要是为了提供自定义的代码来拓展Scrapy的功能,包括下载器中间件和蜘蛛中间件。
17+
18+
#### 数据处理流程
19+
20+
Scrapy的整个数据处理流程由Scrapy引擎进行控制,通常的运转流程包括以下的步骤:
21+
22+
1. 引擎询问蜘蛛需要处理哪个网站,并让蜘蛛将第一个需要处理的URL交给它。
23+
2. 引擎让调度器将需要处理的URL放在队列中。
24+
3. 引擎从调度那获取接下来进行爬取的页面。
25+
4. 调度将下一个爬取的URL返回给引擎,引擎将它通过下载中间件发送到下载器。
26+
5. 当网页被下载器下载完成以后,响应内容通过下载中间件被发送到引擎;如果下载失败了,引擎会通知调度器记录这个URL,待会再重新下载。
27+
6. 引擎收到下载器的响应并将它通过蜘蛛中间件发送到蜘蛛进行处理。
28+
7. 蜘蛛处理响应并返回爬取到的数据条目,此外还要将需要跟进的新的URL发送给引擎。
29+
8. 引擎将抓取到的数据条目送入条目管道,把新的URL发送给调度器放入队列中。
30+
9. 上述操作会一直重复直到调度器中没有需要请求的URL,爬虫停止工作。
31+
32+
### 安装和使用Scrapy
33+
34+
可以先创建虚拟环境并在虚拟环境下使用pip安装scrapy。
35+
36+
```Shell
37+
38+
$
39+
```
40+
41+
项目的目录结构如下图所示。
42+
43+
```Shell
44+
45+
(venv) $ tree
46+
.
47+
|____ scrapy.cfg
48+
|____ qianmu
49+
| |____ spiders
50+
| | |____ __init__.py
51+
| | |____ __pycache__
52+
| |____ __init__.py
53+
| |____ __pycache__
54+
| |____ middlewares.py
55+
| |____ settings.py
56+
| |____ items.py
57+
| |____ pipelines.py
58+
```
59+
60+
> 说明:Windows系统的命令行提示符下有tree命令,但是Linux和MacOS的终端是没有tree命令的,可以用下面给出的命令来定义tree命令,其实是对find命令进行了定制并别名为tree。
61+
>
62+
> `alias tree="find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'"`
63+
>
64+
> Linux系统也可以通过yum或其他的包管理工具来安装tree。
65+
>
66+
> `yum install tree`
67+
68+
根据刚才描述的数据处理流程,基本上需要我们做的有以下几件事情:
69+
70+
1. 在items.py文件中定义字段,这些字段用来保存数据,方便后续的操作。
71+
2. 在spiders文件夹中编写自己的爬虫。
72+
3. 在pipelines.py中完成对数据进行持久化的操作。
73+
4. 修改settings.py文件对项目进行配置。
74+
75+
76+

Day66-75/code/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def main():
123123
spider_thread.start()
124124

125125
while not task_queue.empty() or is_any_alive(spider_threads):
126-
pass
126+
sleep(5)
127127

128128
print('Over!')
129129

Day66-75/code/main_redis.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def is_any_alive(spider_threads):
124124
for spider_thread in spider_threads])
125125

126126

127-
redis_client = redis.Redis(host='120.77.222.217',
127+
redis_client = redis.Redis(host='1.2.3.4',
128128
port=6379, password='1qaz2wsx')
129129
mongo_client = pymongo.MongoClient(host='120.77.222.217', port=27017)
130130
db = mongo_client.msohu

Day66-75/res/scrapy-architecture.jpg

54.5 KB
Loading

0 commit comments

Comments
 (0)