diff --git a/src/Entity/Feed.php b/src/Entity/Feed.php
index 9845e75..8c5136d 100644
--- a/src/Entity/Feed.php
+++ b/src/Entity/Feed.php
@@ -6,6 +6,7 @@ use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Entity\ContentEntityBase;
 use Drupal\Core\Entity\EntityChangedTrait;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
@@ -13,6 +14,7 @@ use Drupal\feeds\Event\DeleteFeedsEvent;
 use Drupal\feeds\Event\FeedsEvents;
 use Drupal\feeds\Exception\LockException;
 use Drupal\feeds\FeedInterface;
+use Drupal\feeds\Feeds\Item\ItemInterface;
 use Drupal\feeds\Feeds\State\CleanState;
 use Drupal\feeds\FeedTypeInterface;
 use Drupal\feeds\Plugin\Type\FeedsPluginInterface;
@@ -272,6 +274,15 @@ class Feed extends ContentEntityBase implements FeedInterface {
   }
 
   /**
+   * @todo describe.
+   */
+  public function invokeEntityEvent($event, EntityInterface $entity, ItemInterface $item) {
+    return $this->entityTypeManager()
+      ->getHandler('feeds_feed', 'feed_import')
+      ->invokeEntityEvent($event, $this, $entity, $item);
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function finishImport() {
diff --git a/src/Event/EntityEvent.php b/src/Event/EntityEvent.php
new file mode 100644
index 0000000..6c31f57
--- /dev/null
+++ b/src/Event/EntityEvent.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Drupal\feeds\Event;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\feeds\FeedInterface;
+use Drupal\feeds\Feeds\Item\ItemInterface;
+
+/**
+ * Fired to begin fetching.
+ */
+class EntityEvent extends EventBase {
+
+  /**
+   * The entity being inserted or updated.
+   *
+   * @var \Drupal\Core\Entity\EntityInterface
+   */
+  protected $entity;
+
+  /**
+   * The item that is being processed.
+   *
+   * @var \Drupal\feeds\Feeds\Item\ItemInterface
+   */
+  protected $item;
+
+  /**
+   * Constructs an EntityEvent object.
+   *
+   * @param \Drupal\feeds\FeedInterface $feed
+   *   The feed.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity being inserted or updated.
+   * @param \Drupal\feeds\Feeds\Item\ItemInterface $item
+   *   The item that is being processed.
+   */
+  public function __construct(FeedInterface $feed, EntityInterface $entity, ItemInterface $item) {
+    parent::__construct($feed);
+    $this->entity = $entity;
+    $this->item = $item;
+  }
+
+  /**
+   * Returns the entity.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The entity being inserted or updated.
+   */
+  public function getEntity() {
+    return $this->entity;
+  }
+
+  /**
+   * Returns the item.
+   *
+   * @return \Drupal\feeds\Feeds\Item\ItemInterface
+   *   The item that is being processed.
+   */
+  public function getItem() {
+    return $this->item;
+  }
+
+}
diff --git a/src/Event/FeedsEvents.php b/src/Event/FeedsEvents.php
index 273e8be..e5bd35d 100644
--- a/src/Event/FeedsEvents.php
+++ b/src/Event/FeedsEvents.php
@@ -67,4 +67,14 @@ final class FeedsEvents {
    */
   const EXPIRE = 'feeds.expire';
 
+  /**
+   * Fired before an entity is validated.
+   */
+  const PROCESS_ENTITY_PREVALIDATE = 'feeds.process_entity_prevalidate';
+
+  /**
+   * Fired before an entity is saved.
+   */
+  const PROCESS_ENTITY_PRESAVE = 'feeds.process_entity_presave';
+
 }
diff --git a/src/FeedImportHandler.php b/src/FeedImportHandler.php
index 8ae32ea..440be28 100644
--- a/src/FeedImportHandler.php
+++ b/src/FeedImportHandler.php
@@ -4,6 +4,7 @@ namespace Drupal\feeds;
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\feeds\Event\CleanEvent;
+use Drupal\feeds\Event\EntityEvent;
 use Drupal\feeds\Event\FeedsEvents;
 use Drupal\feeds\Event\FetchEvent;
 use Drupal\feeds\Event\InitEvent;
@@ -412,6 +413,22 @@ class FeedImportHandler extends FeedHandlerBase {
   }
 
   /**
+   * Invokes an entity event.
+   *
+   * @param string $event
+   *   The event to invoke.
+   * @param \Drupal\feeds\FeedInterface $feed
+   *   The feed in question.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity being inserted or updated.
+   * @param \Drupal\feeds\Feeds\Item\ItemInterface $item
+   *   The item that is being processed.
+   */
+  public function invokeEntityEvent($event, FeedInterface $feed, EntityInterface $entity, ItemInterface $item) {
+    $this->dispatchEvent($event, new EntityEvent($feed, $entity, $item));
+  }
+
+  /**
    * Handles an exception during importing.
    *
    * @param \Drupal\feeds\FeedInterface $feed
diff --git a/src/Feeds/Processor/EntityProcessorBase.php b/src/Feeds/Processor/EntityProcessorBase.php
index f49bd87..4d750f7 100644
--- a/src/Feeds/Processor/EntityProcessorBase.php
+++ b/src/Feeds/Processor/EntityProcessorBase.php
@@ -12,6 +12,8 @@ use Drupal\Core\Entity\Query\QueryFactory;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\feeds\Entity\FeedType;
+use Drupal\feeds\Event\FeedsEvents;
+use Drupal\feeds\Exception\EmptyFeedException;
 use Drupal\feeds\Exception\EntityAccessException;
 use Drupal\feeds\Exception\ValidationException;
 use Drupal\feeds\FeedInterface;
@@ -119,6 +121,7 @@ abstract class EntityProcessorBase extends ProcessorBase implements EntityProces
 
     // Bulk load existing entities to save on db queries.
     if ($skip_existing && $existing_entity_id) {
+      $state->skipped++;
       return;
     }
 
@@ -133,6 +136,7 @@ abstract class EntityProcessorBase extends ProcessorBase implements EntityProces
     // Do not proceed if the item exists, has not changed, and we're not
     // forcing the update.
     if ($existing_entity_id && !$changed && !$this->configuration['skip_hash_check']) {
+      $state->skipped++;
       return;
     }
 
@@ -149,8 +153,17 @@ abstract class EntityProcessorBase extends ProcessorBase implements EntityProces
 
       // Set field values.
       $this->map($feed, $entity, $item);
+
+      // Pass raw item to entity, so entity hook implementations can take action on that.
+      $entity->get('feeds_item')->first()->setItem($item);
+
+      // Validate the entity.
+      $feed->invokeEntityEvent(FeedsEvents::PROCESS_ENTITY_PREVALIDATE, $entity, $item);
       $this->entityValidate($entity);
 
+      // Invoke presave event.
+      $feed->invokeEntityEvent(FeedsEvents::PROCESS_ENTITY_PRESAVE, $entity, $item);
+
       // This will throw an exception on failure.
       $this->entitySaveAccess($entity);
       // Set imported time.
@@ -163,6 +176,10 @@ abstract class EntityProcessorBase extends ProcessorBase implements EntityProces
       $existing_entity_id ? $state->updated++ : $state->created++;
     }
 
+    catch (EmptyFeedException $e) {
+      // Not an error.
+      $state->skipped++;
+    }
     // Something bad happened, log it.
     catch (ValidationException $e) {
       $state->failed++;
diff --git a/src/Plugin/Field/FieldType/FeedsItem.php b/src/Plugin/Field/FieldType/FeedsItem.php
index 12fc6ba..a2f704a 100644
--- a/src/Plugin/Field/FieldType/FeedsItem.php
+++ b/src/Plugin/Field/FieldType/FeedsItem.php
@@ -5,6 +5,7 @@ namespace Drupal\feeds\Plugin\Field\FieldType;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
 use Drupal\Core\TypedData\DataDefinition;
+use Drupal\feeds\Feeds\Item\ItemInterface;
 
 /**
  * Plugin implementation of the 'feeds_item' field type.
@@ -24,6 +25,13 @@ use Drupal\Core\TypedData\DataDefinition;
 class FeedsItem extends EntityReferenceItem {
 
   /**
+   * The item, available during processing.
+   *
+   * @var \Drupal\feeds\Feeds\Item\ItemInterface
+   */
+  protected $item;
+
+  /**
    * {@inheritdoc}
    */
   public function setValue($values, $notify = TRUE) {
@@ -124,4 +132,24 @@ class FeedsItem extends EntityReferenceItem {
     $this->imported = (int) $this->imported;
   }
 
+  /**
+   * Sets the raw item on the feed type.
+   *
+   * @param \Drupal\feeds\Feeds\Item\ItemInterface
+   *   The item to set.
+   */
+  public function setItem(ItemInterface $item) {
+    $this->item = $item;
+  }
+
+  /**
+   * Returns the raw item, if available.
+   *
+   * @return \Drupal\feeds\Feeds\Item\ItemInterface | null
+   *   The item, if available. Null otherwise.
+   */
+  public function getItem() {
+    return $this->item;
+  }
+
 }
