czwartek, 22 listopada 2012, 21:37

web scraping dla ubogich

W poprzednim odcinku odkryłem webofstories, prawdziwą kopalnię wiedzy np. o kulisach rewolucji w biologii w drugiej połowie XX wieku. Ale lubię mieć wszystko na piśmie i cuzamen do kupy, a tam każda wypowiedź poszatkowana na sto kilka odcinków. Próbowałem sklejać na piechotę, ale od takiej głupiej roboty jest przecież maszyna. Mam sto ileś kolejnych stron, na każdej interesujący fragment jednoznacznie zaznaczony tagiem id="transcript-en", trzeba je tylko wydobyć i skleić. Byle skrypt wystarczy. A dobrzy ludzie jak zwykle zrobili już prawie wszystko za mnie, wystarczyło znaleźć i zaadoptować. Gdyby ktoś chciał skorzystać to krok po kroku:

1. zainstalować interpretaror Lua, najbezpieczniej 5.15. Tak żeby jak się napisze lua w lini poleceń wyskakiwało:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio

2. zainstalować luarocks i potem luacurl (luarocks install luacurl). Można też samo luacurl, chodzi o dostęp z lua do biblioteki curla (każdy porządny człowiek powinien go już mieć na swoim pececie).

3. ustalić na webofostories zakres interesujących stron, od pierwszej do ostatniej. Upewnić się że idą po kolei, bez dziur (nie zawsze tak jest i wtedy trzeba na raty), skoczyć na koniec załączonego skryptu i tam wpisać. Na początek warto sprawdzić na kilku czy w ogóle działa. U mnie zwykle działa. Żeby ściągnąć polskie transkrypcje (są np. przy Edelmanie i Wajdzie) trzeba zamienić transcript%-en na transcript%-pl

4. jeśli działa to puścić skrypt zapisując wszystko na dysku (lua skrypt.lua > plik.html)


-- poniżej ukradłem stąd: https://github.com/mkottman/wdm

pcall(require, 'luacurl')
assert(curl, "curl library not found (luacurl)")

function log(...)
  if verbose then
    print(...)
  end
end

-- HTTP Downloading
do
  local c=curl.new and curl.new() or curl.easy_init()

  local filters = {}
  function addFilter(f) table.insert(filters, f) end
  function clearFilters() filters = {} end

  do
    local f = io.open('cache/TEST.txt', 'w')
    if not f then
      os.execute('mkdir cache')
    else
      f:close()
      os.remove('cache/TEST.txt')
    end
  end
  
  local function open(fn, mode)
    return bz2 and bz2.open(fn, mode, 9) or io.open(fn, mode)
  end

  local function getlocal(url)
    local path = url:gsub('[^%a%d]', '_')
    local f, e = open('cache/'..path)
    if f then
      local ret = f:read('*a')
      f:close()
      return ret
    end
  end

  local function writelocal(url, s)
    local path = url:gsub('[^%a%d]', '_')
    local f = assert(open('cache/'..path, 'wb'))
    f:write(s)
    f:close()
  end

  function get(url)
    log('[http]', 'get', url)

    local cache = getlocal(url)
    if cache then return cache end

    c:setopt(curl.OPT_URL,url)
    local t = {}
    c:setopt(curl.OPT_WRITEFUNCTION, function (a, b)
      local s
      -- luacurl and Lua-cURL friendly
      if type(a) == "string" then s = a else s = b end
      table.insert(t, s)
      return #s
    end)
    assert(c:perform())
    local ret = table.concat(t)
    for _,f in ipairs(filters) do
      ret = f(ret)
    end

    writelocal(url, ret)

    return ret
  end
end

-- koniec ukradzionego 


local strona = 'http://www.webofstories.com/play/'

print ('<!DOCTYPE html>') 

-- organoleptycznie ustalone zakresy wybranych pogadanek
-- np. Brenner: 13214-13355 52393-52486 
-- Klug: 16940-17059
-- Mitchison: 13765-13843 52542-52582
-- poniżej ustawione na ściągnięcie trzech pierwszych kawałków Kluga

for i = 16940, 16942, 1 do -- tu ustaw ręcznie zakres
  nr = string.format("%01d",i) -- tu ewentualnie zera przed
  local src = get(strona .. nr)
  _, _, transc, _ = string.match(src, '(<div id="transcript%-en" class="transcriptText">)(%s+)(.-)(</div>)') 
  print(transc) 
end


DODANE: z pojedynczej strony wyłuskać transkrypt można jednym poleceniem, od tego pewnie warto zacząć, wystarczy tylko curl i lua połączone rurką, np.:

curl http://www.webofstories.com/play/sydney.brenner/161|lua -e '_,_,x,_=string.match(io.read("*all"),[[(<div id="transcript%-en" class="transcriptText">)(%s+)(.-)(</div>)]]) print(x)'
czyli (to samo z zawinięciem linii, bo nie wszystkie przeglądarki zawijają):

curl http://www.webofstories.com/play/sydney.brenner/161|lua -e '_,_,x,_=string.match(io.read("*all"),[[(<div id="transcript%-en" class="transcriptText">)(%s+)(.-)(</div>)]]) print(x)'

Nie wiem dlaczego na Windows w czarnym okienku CMD to nie działa, ale jakoś mnie to nie dziwi, jak ktoś musi na Windows to jest np. bash od Git i pewnie jeszcze wiele innych, których nie sprawdzałem.

DODANE (2018): Jeśli ktoś naprawdę musi na Windows (7/8/10), to polecam MSYS2. W Windows 10 jest też Subsystem for Linux.

Brak komentarzy: