웹 크롤링

Python을 이용해 웹 페이지에서 원하는 정보를 추출해 내는 코드다. 아래는 네이버 뉴스의 오늘 날짜에 대한 토픽 제목과 해당 url을 뽑아내는 코드 예제.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import requests
from bs4 import BeautifulSoup
url = 'https://news.naver.com/main/ranking/popularDay.nhn?rankingType=popular_day&date=20191022'
r = requests.get(url)
html = r.content
soup = BeautifulSoup(html, 'html.parser')
titles_html = soup.select('.ranking_section > ol > li > dl > dt > a')
for i in range(len(titles_html)):
print('#' + str(i+1), titles_html[i].text)
print(titles_html[i]['href'])
print()
import requests from bs4 import BeautifulSoup url = 'https://news.naver.com/main/ranking/popularDay.nhn?rankingType=popular_day&date=20191022' r = requests.get(url) html = r.content soup = BeautifulSoup(html, 'html.parser') titles_html = soup.select('.ranking_section > ol > li > dl > dt > a') for i in range(len(titles_html)): print('#' + str(i+1), titles_html[i].text) print(titles_html[i]['href']) print()
import requests
from bs4 import BeautifulSoup

url = 'https://news.naver.com/main/ranking/popularDay.nhn?rankingType=popular_day&date=20191022'

r = requests.get(url)
html = r.content
soup = BeautifulSoup(html, 'html.parser')
titles_html = soup.select('.ranking_section > ol > li > dl > dt > a')

for i in range(len(titles_html)):
    print('#' + str(i+1), titles_html[i].text)
    print(titles_html[i]['href'])
    print()

결과는 대략 아래와 같다.

#1 러 군용기 6대의 무력 시위···한반도 3면 다 헤집고 다녔…
/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=025&aid=0002946814&date=20191022&type=1&rankingSectionId=100&rankingSeq=1

#2 시정연설 마친 文, 이철희에 건넨 말 "섭섭한가 시원한가"
/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=025&aid=0002946806&date=20191022&type=1&rankingSectionId=100&rankingSeq=2

.
.

#5 文대통령 발언에 뒤집어진 ‘정시 확대’…“조국 사태가 교육…
/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=020&aid=0003248596&date=20191022&type=1&rankingSectionId=100&rankingSeq=5

C#의 Resource Pool (리소스 풀)

C# 언어를 활용하여 스레드를 통해 코드를 동시에 실행하고자 하는데, DB Connection과 같은 유한한 자원을 미리 생성해 놓고 활용하고자 합니다. 이때 사용할 리소스 풀로써 만든 클래스입니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class ResourcePool<T>
{
private readonly ConcurrentBag<T> _items = new ConcurrentBag<T>();
private ManualResetEvent _event = new ManualResetEvent(true);
public ResourcePool(List<T> items) {
var iter = items.GetEnumerator();
while(iter.MoveNext())
{
_items.Add(iter.Current);
}
}
public void Release(T item)
{
_items.Add(item);
_event.Set();
}
public T Get()
{
T item;
do
{
if (_items.IsEmpty)
{
_event.Reset();
_event.WaitOne();
}
} while (!_items.TryTake(out item));
return item;
}
}
class ResourcePool<T> { private readonly ConcurrentBag<T> _items = new ConcurrentBag<T>(); private ManualResetEvent _event = new ManualResetEvent(true); public ResourcePool(List<T> items) { var iter = items.GetEnumerator(); while(iter.MoveNext()) { _items.Add(iter.Current); } } public void Release(T item) { _items.Add(item); _event.Set(); } public T Get() { T item; do { if (_items.IsEmpty) { _event.Reset(); _event.WaitOne(); } } while (!_items.TryTake(out item)); return item; } }
class ResourcePool<T>
{
    private readonly ConcurrentBag<T> _items = new ConcurrentBag<T>();
    private ManualResetEvent _event = new ManualResetEvent(true);

    public ResourcePool(List<T> items) {
        var iter = items.GetEnumerator();

        while(iter.MoveNext())
        {
            _items.Add(iter.Current);
        }
    }

    public void Release(T item)
    {
        _items.Add(item);
        _event.Set();
    }

    public T Get()
    {
        T item;

        do
        {
            if (_items.IsEmpty)
            {
                _event.Reset();
                _event.WaitOne();
            }
        } while (!_items.TryTake(out item));
            
        return item;
    }
}

위의 ResourcePool은 generic 클래스로 어떤 객체 타입이든 리소스로써 담아두고 활용할 수 있습니다. 예를 들어, 아래와 같은 클래스로 말입니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Resource
{
public int v {
get;
set;
}
private int _useCount = 0;
public Resource(int v)
{
this.v = v;
}
public void increaseUsingCount()
{
_useCount++;
}
public string toString()
{
return "V: " + v + " Count: " + _useCount;
}
}
class Resource { public int v { get; set; } private int _useCount = 0; public Resource(int v) { this.v = v; } public void increaseUsingCount() { _useCount++; } public string toString() { return "V: " + v + " Count: " + _useCount; } }
class Resource
{
    public int v {
        get;
        set;
    }

    private int _useCount = 0;

    public Resource(int v)
    {
        this.v = v;
    }

        
    public void increaseUsingCount()
    {
        _useCount++;
    }

    public string toString()
    {
        return "V: " + v + " Count: " + _useCount;
    }
}

이 리소스 풀에 스레드에서 사용할 리소스를 담아두는 코드의 예는 아래와 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
List<Resource> items = new List<Resource>() { new Resource(0), new Resource(1), new Resource(2), new Resource(3) };
ResourcePool<Resource> objPool = new ResourcePool<Resource>(items);
List<Resource> items = new List<Resource>() { new Resource(0), new Resource(1), new Resource(2), new Resource(3) }; ResourcePool<Resource> objPool = new ResourcePool<Resource>(items);
List<Resource> items = new List<Resource>() { new Resource(0), new Resource(1), new Resource(2), new Resource(3) };
ResourcePool<Resource> objPool = new ResourcePool<Resource>(items);

실제 리소스 풀은 스레드를 통해 활용될 때에 그 의미가 있습니다. 예를 들어 앞서 작성한 코드들을 전제로 아래의 코드처럼 말입니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Parallel.For(1, 50, new ParallelOptions { MaxDegreeOfParallelism = 8 },
(int i) => {
Resource r = objPool.Get();
r.increaseUsingCount();
Console.WriteLine("[Thread-Index: " + i + "] " + r.toString());
// Some Operation
Thread.Sleep(new Random().Next(600, 3000));
// .
objPool.Release(r);
});
Parallel.For(1, 50, new ParallelOptions { MaxDegreeOfParallelism = 8 }, (int i) => { Resource r = objPool.Get(); r.increaseUsingCount(); Console.WriteLine("[Thread-Index: " + i + "] " + r.toString()); // Some Operation Thread.Sleep(new Random().Next(600, 3000)); // . objPool.Release(r); });
Parallel.For(1, 50, new ParallelOptions { MaxDegreeOfParallelism = 8 },
    (int i) => {
        Resource r = objPool.Get();

        r.increaseUsingCount();
        Console.WriteLine("[Thread-Index: " + i + "] " + r.toString());

        // Some Operation
        Thread.Sleep(new Random().Next(600, 3000));
        // .

        objPool.Release(r);
    });

위의 코드는 총 49개의 스레드를 만들고, MaxDegreeOfParallelism에서 지정한 값이 8만큼, 최대 8개의 스레드만을 동시에 실행하도록 하며, 람다 함수를 통해 실행할 스레드 코드를 지정하고 있습니다.