[PHP] URLs in tekst vinden en vervangen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
Ik ben bezig met een functie die in een string alle URLs opzoekt, kijkt of die URLs "shortened" zijn (bv. http://bit.ly/fd4GDs) of "gewoon" (http://tweakers.net), en de shortened URLs vervangt door de resolved URL. Ik ben hierin redelijk geslaagd, behalve voor het laatste deel:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function reverse_urls($text) {
  preg_match_all('!http://[\S]+!', $text, $matches);
  foreach ($matches[0] as $url) {
    $headers = get_headers($url, 1);
    if (!empty($headers['Location'])) { // Probably shortened URL
      $headers['Location'] = (array) $headers['Location'];
      $return[] = array_pop($headers['Location']); // Handle multiple redirects
    }
    else { // Regular URL
      $return[] = $url;
    }
  }
  print_r($return);
}

$string = "Text met shortened http://read.bi/fRO2yK en normale http://www.nu.nl urls";
// http://read.bi/fRO2yK leidt naar http://www.businessinsider.com/huffington-post-investors-2011-2

reverse_urls($text);

De bedoeling is dus dat reverse_urls() als output geeft:
code:
1
2
Text met shortened http://www.businessinsider.com/huffington-post-investors-2011-2 
en normale http://www.nu.nl urls

Dat laatste stuk, het vervangen van "shortened" door "resolved" URLs, lukt nog niet. Dit zou kunnen met preg_replace of preg_replace_callback (ipv eerst matchen met preg_match_all), maar preg_replace en preg_replace_callback stoppen met werken nadat ze de eerste URL gematched hebben, en ik zie even niet hoe ik hier omheen kan werken. Aan de andere kant loop ik nu ook vast; dat ik de "resolved" URLs in een array heb ik leuk, maar hoe vervang ik nu de URLs in de tekst?

P.S. Ik weet dat de regexp die ik gebruik slechts heel rudimentair is, maar dat is een zorg voor later :) (ik ben nu eenmaal niet zo'n regexp held).

P.P.S. In plaats van de headers aan te vragen van elke URL, zou ik ook van tevoren kunnen bepalen of een URL van een shortening service is, maar het is haast onmogelijk om daar een uitputtende array van te geven waartegen je kunt matchen...

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


Acties:
  • 0 Henk 'm!

  • GlowMouse
  • Registratie: November 2002
  • Niet online
Reveller schreef op zaterdag 12 februari 2011 @ 14:53:
maar preg_replace en preg_replace_callback stoppen met werken nadat ze de eerste URL gematched hebben, en ik zie even niet hoe ik hier omheen kan werken.
http://nl.php.net/preg_replace_callback
The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit).
:?

Acties:
  • 0 Henk 'm!

  • ray538
  • Registratie: Januari 2010
  • Laatst online: 19-09 16:34
Reveller schreef op zaterdag 12 februari 2011 @ 14:53:
preg_replace en preg_replace_callback stoppen met werken nadat ze de eerste URL gematched hebben
Mijn ervaring is dat je altijd een beetje moet spelen met het vraagteken (ungreedy) en de modifiers.

Je kan een vraagteken achter de quantifier (dit zijn oa: +, * en {getal} ) zetten, dat maakt je pattern ungreedy.
Of je probeert een of meerdere modifiers uit: http://php.net/manual/en/...cre.pattern.modifiers.php

Ik denk dat vooral dat laatste de oplossing is voor jou.

[ Voor 29% gewijzigd door ray538 op 12-02-2011 15:07 . Reden: Toevoeging ]


Acties:
  • 0 Henk 'm!

  • drm
  • Registratie: Februari 2001
  • Laatst online: 09-06 13:31

drm

f0pc0dert

Als je oorspronkelijke url weet, en je weet waardoor je hem moet vervangen, kun je toch gewoon een str_replace doen?

Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz


Acties:
  • 0 Henk 'm!

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
drm schreef op zaterdag 12 februari 2011 @ 15:46:
Als je oorspronkelijke url weet, en je weet waardoor je hem moet vervangen, kun je toch gewoon een str_replace doen?
Oh ja, natuurlijk :) Helemaal niet aangedacht en werkt perfect.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
function reverse_urls($text) {
  preg_match_all('!http://[\S]+!', $text, $matches);
  foreach ($matches[0] as $url) {
    $headers = get_headers($url, 1);
    if (!empty($headers['Location'])) { // Probably shortened URL
      $headers['Location'] = (array) $headers['Location'];
      $search[] = $url;
      $replace[] = array_pop($headers['Location']); // Handle multiple redirects
    }
  }
  return str_replace($search, $replace, $text);
}

Ik was trouwens niet duidelijk in de openingspost. preg_replace en preg_replace_callback hebben idd een ongelimiteerd aantal mogelijke replacements, maar mijn probleem is dit:
PHP:
1
2
3
4
5
6
7
8
function reverse_urls_($text) {
  $text = preg_replace_callback(
    '!http://[\S]+!', 
    create_function('$matches', 'return function_to_replace_url($matches[0]);'),
    $text
  );
  return $text;
}

Hoe ik het zie heb ik altijd een tweede functie nodig die elke gevonden URL omzet naar een "uitgepakte" URL; iets wat ik nu binnen 1 functie kan doen. Of snap ik het dan niet?

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


Acties:
  • 0 Henk 'm!

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
Overigens heeft get_headers() als nadeel dat de timeout niet is in te stellen. Een tekst met een aantal links, waarvan er 1 of 2 niet (meteen) resolven, resulteert in enorme vertragingen of errors. Vandaar leek deze oplossing mij beter:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function reverse_urls($text) {
  preg_match_all('!http://[\S]+!', $text, $matches);
  $search = array();
  $replace = array();
  foreach ($matches[0] as $url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_NOBODY, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 1);

    $result = curl_exec($ch); 

    if (preg_match("/Location\:/","$result")) {
      $_url = explode("Location: ",$result);
      $reversed_url = explode("\r",$_url[1]);
      $search[] = $url;
      $replace[] = $reversed_url[0];
    }
  }
  return str_replace($search, $replace, $text);
}

Graag op- of aanmerkingen hierop; verbeteringen zijn welkom! :)

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


Acties:
  • 0 Henk 'm!

  • Raynman
  • Registratie: Augustus 2004
  • Laatst online: 01:02
Reveller schreef op zaterdag 12 februari 2011 @ 16:05:
Hoe ik het zie heb ik altijd een tweede functie nodig die elke gevonden URL omzet naar een "uitgepakte" URL; iets wat ik nu binnen 1 functie kan doen. Of snap ik het dan niet?
Niet helemaal in elk geval. Je hebt inderdaad een callback-functie nodig, maar met create_function kun je ter plekke een anonieme functie maken, waardoor dus toch alles binnen reverse_urls blijft staan. Jij maakt nu echter een anonieme functie die vrijwel niets doet (alleen (een deel van) de parameters doorgeven), zodat je toch nog een 'globale' functie nodig hebt.
Edit: in nieuwe PHP-versies kun je ook anonieme functies gebruiken zie ik (nieuwe syntax ipv create_function); staat wat netter dan een functiedefinitie tussen aanhalingstekens.

En je kunt gewoon de edit-knop gebruiken als je nog een 'overigens...' wilt toevoegen.

[ Voor 10% gewijzigd door Raynman op 13-02-2011 00:47 ]

Pagina: 1